diff --git a/.gitignore b/.gitignore index 3ddb942c4..b33099105 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,11 @@ build/ venv/ .venv/ .idea/ -results/ logs/ +vectordb_bench/results/cloudleaderboard/ + +# Worktrees +.worktrees/ # AI rules CLAUDE.md diff --git a/Makefile b/Makefile index e227e3b92..ef8207c55 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,10 @@ -lint: - PYTHONPATH=`pwd` python3 -m black vectordb_bench --check - PYTHONPATH=`pwd` python3 -m ruff check vectordb_bench - unittest: PYTHONPATH=`pwd` python3 -m pytest tests/test_dataset.py::TestDataSet::test_download_small -svv -.PHONY: lint unittest +format: + PYTHONPATH=`pwd` python3 -m black vectordb_bench + PYTHONPATH=`pwd` python3 -m ruff check vectordb_bench --fix + +lint: + PYTHONPATH=`pwd` python3 -m black vectordb_bench --check + PYTHONPATH=`pwd` python3 -m ruff check vectordb_bench diff --git a/README.md b/README.md index ef498c263..85d34d76d 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,11 @@ Prepare to delve into the world of VDBBench, and let it guide you in uncovering VDBBench is sponsored by Zilliz,the leading opensource vectorDB company behind Milvus. Choose smarter with VDBBench - start your free test on [zilliz cloud](https://zilliz.com/) today! **Leaderboard:** https://zilliz.com/benchmark + +## 🎈 Announcement 🎈 + +**June 2026 update:** Full Text Search has landed in VectorDBBench. We now benchmark BM25-style retrieval across supported backends, starting with MS MARCO and HotpotQA datasets, payload profiles, recall, QPS, and load metrics ready to compare. See the [VectorDBBench Full Text Search Release Note](docs/release/2026-06-full-text-search.md) for the full rollout details and caveats. + ## Quick Start ### Prerequirement ``` shell @@ -27,11 +32,6 @@ python >= 3.11 pip install vectordb-bench ``` -**Install all database clients** - -``` shell -pip install 'vectordb-bench[all]' -``` **Install the specific database client** ```shell @@ -42,12 +42,11 @@ All the database client supported | Optional database client | install command | |--------------------------|---------------------------------------------| | pymilvus, zilliz_cloud (*default*) | `pip install vectordb-bench` | -| all (*clients requirements might be conflict with each other*) | `pip install vectordb-bench[all]` | | qdrant | `pip install vectordb-bench[qdrant]` | | pinecone | `pip install vectordb-bench[pinecone]` | | weaviate | `pip install vectordb-bench[weaviate]` | | elastic, aliyun_elasticsearch| `pip install vectordb-bench[elastic]` | -| pgvector, pgvectorscale, pgdiskann, alloydb | `pip install vectordb-bench[pgvector]` | +| pgvector, pgvectorscale, pgdiskann, alloydb, vectorchord | `pip install vectordb-bench[pgvector]` | | pgvecto.rs | `pip install vectordb-bench[pgvecto_rs]` | | redis | `pip install vectordb-bench[redis]` | | memorydb | `pip install vectordb-bench[memorydb]` | @@ -61,11 +60,14 @@ All the database client supported | oceanbase | `pip install vectordb-bench[oceanbase]` | | hologres | `pip install vectordb-bench[hologres]` | | tencent_es | `pip install vectordb-bench[tencent_es]` | -| alisql | `pip install 'vectordb-bench[alisql]'` | +| alisql | `pip install vectordb-bench[alisql]` | +| polardb | `pip install vectordb-bench[polardb]` | | doris | `pip install vectordb-bench[doris]` | | zvec | `pip install vectordb-bench[zvec]` | | endee | `pip install vectordb-bench[endee]` | | lindorm | `pip install vectordb-bench[lindorm]` | +| volc_mysql | `pip install vectordb-bench[volc_mysql]` | +| adbpg | `pip install vectordb-bench[adbpg]` | ### Run @@ -91,11 +93,21 @@ Options: Commands: pgvectorhnsw pgvectorivfflat + vectorchordrq + volcmysqlhnsw test weaviate ``` To list the options for each command, execute `vectordbbench [command] --help` +Use `--note` or `--note-file` to preserve deployment, resource, client, network, and constraint context in each result JSON under `task_config.db_config.note`. The options are mutually exclusive. Prefer `--note-file` for structured or multiline context, and never include credentials, tokens, or sensitive connection details. + +```shell +vectordbbench zillizautoindex \ + --note-file ./run-context.json \ + +``` + ```text $ vectordbbench pgvectorhnsw --help Usage: vectordbbench pgvectorhnsw [OPTIONS] @@ -114,6 +126,9 @@ Options: Case type --db-label TEXT Db label, default: date in ISO format [default: 2024-05-20T20:26:31.113290] + --note TEXT Run context stored with each result + [default: ""] + --note-file FILE Read run context from a UTF-8 text file --dry-run Print just the configuration and exit without running the tasks --k INTEGER K value for number of nearest neighbors to @@ -184,6 +199,34 @@ Options: --help Show this message and exit. ``` +### Run VectorChord (vchordrq) from command line + +VectorChord is a PostgreSQL extension for scalable vector similarity search using IVF + RaBitQ indexing. +It is fully compatible with pgvector data types and provides faster queries and index builds. + +```shell +vectordbbench vectorchordrq \ + --user-name postgres --password '' \ + --host localhost --port 5432 --db-name vectordb \ + --case-type Performance1536D50K \ + --lists 1000 --probes 10 --epsilon 1.9 \ + --spherical-centroids --build-threads 8 \ + --max-parallel-workers 15 +``` + +Key VectorChord-specific options: +| Option | Description | +|--------|-------------| +| `--lists` | Number of IVF lists for vchordrq index | +| `--probes` | Number of probes during search (default: 10) | +| `--epsilon` | Reranking precision factor, 0.0-4.0 (default: 1.9) | +| `--residual-quantization` | Enable residual quantization | +| `--spherical-centroids` | L2-normalize centroids (recommended for cosine/IP) | +| `--build-threads` | Number of threads for index building (1-255) | +| `--degree-of-parallelism` | Degree of parallelism for index build (1-256) | +| `--max-parallel-workers` | Sets max_parallel_workers & max_parallel_maintenance_workers | +| `--max-scan-tuples` | Max tuples to scan before stopping (-1 for unlimited) | + ### Run awsopensearch from command line ```shell @@ -225,12 +268,49 @@ Options: --ondisk Ondisk mode with binary quantization(32x compression) --oversample-factor Controls the degree of oversampling applied to minority classes in imbalanced datasets to improve model performance by balancing class distributions.(default 1.0) - # Quantization Type --quantization-type TEXT which type of quantization to use valid values [fp32, fp16, bq] --help Show this message and exit. ``` + +### Run awsopensearch serverless from command line + +OpenSearch Serverless (AOSS) is a serverless deployment option for Amazon OpenSearch Service. VDBBench supports AOSS with the `--serverless` flag, which uses AWS SigV4 authentication and automatically skips unsupported operations (cluster settings, force merge, manual refresh, warmup API). + +**Prerequisites:** +- AWS credentials configured (via `~/.aws/credentials`, environment variables, or IAM role) +- Serverless dependencies installed (included in the `opensearch` extra): `pip install 'vectordb-bench[opensearch]'`. This installs `opensearch-py`, `boto3`, and `requests-aws4auth`. +- IAM identity policy allowing `aoss:APIAccessAll` on the collection +- AOSS Data Access Policy granting index/collection permissions to the IAM principal + +**Example: Run performance test on OpenSearch Serverless** + +```shell +NUM_PER_BATCH=100 vectordbbench awsopensearch --db-label aoss \ + --serverless --aws-region us-east-1 \ + --host .aoss.us-east-1.on.aws --port 443 \ + --case-type Performance768D1M \ + --m 16 --ef-construction 200 --ef-search 40 \ + --number-of-shards 8 --number-of-replicas 0 \ + --engine faiss --metric-type cosine \ + --num-concurrency 80,100,120 +``` + +OpenSearch Serverless-specific options: + +| Option | Description | +|--------|-------------| +| `--serverless` | Enable OpenSearch Serverless mode (uses AWS SigV4 auth) | +| `--aws-region` | AWS region for the AOSS collection (default: `us-east-1`) | +| `NUM_PER_BATCH` | Number of vectors per Serverless bulk request (default: `100`) | + +> **Notes:** +> - `--user` and `--password` are not needed for Serverless mode +> - `--engine` is accepted but ignored internally (AOSS manages the engine) +> - `--force-merge-enabled`, `--refresh-interval`, `--flush-threshold-size`, and `--cb-threshold` are ignored for Serverless +> - Keep `NUM_PER_BATCH` small enough for the Serverless bulk API request limits + ### Run Elastic Cloud from command line Elastic Cloud supports multiple index types: HNSW, HNSW_INT8, HNSW_INT4, and HNSW_BBQ. @@ -294,13 +374,13 @@ Options: # Connection --cloud-id TEXT Elastic Cloud ID [required] --password TEXT Elastic Cloud password [required] - + # HNSW Index Parameters --m INTEGER HNSW M parameter [default: 16] --ef-construction INTEGER HNSW efConstruction parameter [default: 100] --num-candidates INTEGER Number of candidates for search [default: 100] --element-type [float|byte] Element type for vectors (float: 4 bytes, byte: 1 byte) [default: float] - + # Index Configuration --number-of-shards INTEGER Number of shards [default: 1] --number-of-replicas INTEGER Number of replicas [default: 0] @@ -311,7 +391,7 @@ Options: --use-routing BOOLEAN Whether to use routing [default: False] --use-rescore BOOLEAN Whether to use rescore [default: False] --oversample-ratio FLOAT Oversample ratio for rescore [default: 2.0] - + # Common Options --case-type [CapacityDim128|CapacityDim960|Performance768D100M|...] Case type @@ -401,7 +481,8 @@ Execute tests for the index types: HGraph. NUM_PER_BATCH=10000 vectordbbench hologreshgraph --host Hologres_Endpoint --port 80 \ --user ACCESS_ID --password ACCESS_KEY --database DATABASE_NAME \ --m 64 --ef-construction 400 --case-type Performance768D10M \ ---index-type HGraph --ef-search 400 --k 10 --num-concurrency 1,60,70,75,80,90,95,100,110,120 +--index-type HGraph --ef-search 400 --k 10 --num-concurrency 1,60,70,75,80,90,95,100,105,110,115,120,125,130 \ +--serial-cooldown 3 ``` To list the options for Hologres, execute `vectordbbench hologreshgraph --help`, The following are some Hologres-specific command-line options. @@ -491,7 +572,7 @@ Lindorm supports index types: hnsw, ivfpq, or ivfbq. ```shell vectordbbench lindormhnsw --case-type Performance768D10M --index-name --k 10 \ --host --port --user --password --m 32 \ ---ef-construction 400 --ef-search 150 +--ef-construction 400 --ef-search 150 --reorder-factor 2 ``` **Example: Run ivfpq index test** @@ -525,8 +606,127 @@ To list the options for Lindorm, execute `vectordbbench lindormhnsw --help`, The --m INTEGER hnsw m [required] --ef-construction INTEGER hnsw ef-construction [required] --ef-search INTEGER hnsw ef-search [required] + --reorder-factor INTEGER reorder factor +``` + +### Run ADBPG (Aliyun AnalyticDB for PostgreSQL) from command line + +ADBPG Nova uses the fastann/Nova vector index engine with `USING ann` syntax. + +**Example: Run novamr index benchmark (BioASQ 1M, 1024-dim)** + +```shell +vectordbbench adbpgnova --case-type Performance1024D1M --k 10 \ +--host --port 5432 --db-name postgres \ +--user-name --password \ +--algorithm novamr --hnsw-m 48 --ef-construction 600 \ +--ef-search 130 --max-scan-points 5000 --quantize-rescore-amp 2.0 ``` +**Example: Run from config file** + +```shell +vectordbbench adbpgnova --config-file adbpg_bioasq1m_novamr.yml +``` + +To list the options for ADBPG, execute `vectordbbench adbpgnova --help`. The following are some ADBPG-specific command-line options. + +```text + --user-name TEXT Db username [required] + --password TEXT Postgres database password [$POSTGRES_PASSWORD] + --host TEXT Db host [required] + --port INTEGER Postgres database port [default: 5432] + --db-name TEXT Db name [required] + --algorithm TEXT algorithm [default: novamr] + --hnsw-m INTEGER hnsw_m [default: 16] + --ef-construction INTEGER ef_construction [default: 200] + --ef-search INTEGER ef_search [default: 100] + --max-scan-points INTEGER max scan points [default: 2000] + --quantize-rescore-amp FLOAT fastann.quantize_rescore_amp [default: 1.0] + --nova-adaptive-gamma FLOAT fastann.nova_adaptive_gamma [default: 0.0] + --auto-reduction/--no-auto-reduction Index WITH auto_reduction=on [default: False] +``` + +### Run PolarDB from command line + +PolarDB supports index types: faiss_hnsw_flat, faiss_hnsw_pq, and faiss_hnsw_sq. + +**Example: Run faiss_hnsw_flat benchmark** + +```shell +vectordbbench polardbhnswflat \ + --case-type Performance768D1M \ + --username \ + --password '' \ + --host \ + --port 3306 \ + --m 16 \ + --ef-construction 256 \ + --ef-search 256 \ + --insert-workers 64 \ + --num-concurrency '10,20,40,60,80' \ + --concurrency-duration 60 \ + --task-label \ + --db-label \ + --skip-search-serial \ + --post-load-index +``` + +To list the options for PolarDB, execute `vectordbbench polardbhnswflat --help`. The following are some PolarDB-specific command-line options. + +```text + --username TEXT Username [required] + --password TEXT Password + --host TEXT Db host [default: 127.0.0.1] + --port INTEGER Db Port [default: 3306] + --database TEXT Database name [default: vectordbbench] + --m INTEGER M parameter (max_degree) in HNSW + --ef-construction INTEGER ef_construction parameter in HNSW + --ef-search INTEGER polar_vector_index_hnsw_ef_search session variable + --insert-workers INTEGER Number of concurrent threads for data insertion + --post-load-index / --inline-index + Create index after load or inline at table creation +``` + +### Run VolcMySQL from command line + +VolcMySQL is a MySQL-compatible service with a native `VECTOR` type and an HNSW vector index (created via `SECONDARY_ENGINE_ATTRIBUTE`). Optional quantization is configurable through `--quant-algorithm` (`NONE`, `SQ`, `PQ`) and `--quant-type` (`16_bit`, `8_bit`, `4_bit`, `binary`). + +```shell +vectordbbench volcmysqlhnsw \ + --case-type Performance1536D50K \ + --username \ + --password '' \ + --host \ + --port 3306 \ + --m 16 \ + --ef-construction 128 \ + --ef-search 100 \ + --quant-algorithm SQ \ + --quant-type 16_bit \ + --num-concurrency '10,20,40,60,80' \ + --concurrency-duration 30 \ + --task-label \ + --db-label +``` + +To list the options for VolcMySQL, execute `vectordbbench volcmysqlhnsw --help`. The following are some VolcMySQL-specific command-line options. + +```text + --username TEXT Username [required] + --password TEXT Password [required] + --host TEXT Db host [default: 127.0.0.1] + --port INTEGER DB Port [default: 3306] + --m INTEGER M parameter in HNSW vector indexing + --ef-search INTEGER Session variable loose_hnsw_ef_search + --ef-construction INTEGER HNSW ef_construction + --quant-algorithm [NONE|SQ|PQ] Quantization algorithm + --quant-type [16_bit|8_bit|4_bit|binary] + Quantization type +``` + +> Note: vectors are loaded and queried over the raw-binary `VECTOR` path by default; the client auto-probes server support and falls back to `to_vector()` text when unavailable. Set `VDB_BINARY_VEC=0` to force the text path or `1` to force binary. + #### Using a configuration file. The vectordbbench command can optionally read some or all the options from a yaml formatted configuration file. @@ -644,6 +844,17 @@ vectordbbench batchcli --batch-config-file ### Introduction To facilitate the presentation of test results and provide a comprehensive performance analysis report, we offer a [leaderboard page](https://zilliz.com/benchmark). It allows us to choose from QPS, QP$, and latency metrics, and provides a comprehensive assessment of a system's performance based on the test results of various cases and a set of scoring mechanisms (to be introduced later). On this leaderboard, we can select the systems and models to be compared, and filter out cases we do not want to consider. Comprehensive scores are always ranked from best to worst, and the specific test results of each query will be presented in the list below. +### Cloud Leaderboard + +VectorDBBench now includes Cloud Leaderboard cases for production-oriented cloud vector database evaluation. These cases complement the original raw-performance leaderboard by measuring behaviors that matter for managed services: + +- `CloudInsertCase`: insert throughput plus searchable and indexed readiness delays. +- `CloudPayloadSearchCase`: search performance when responses return IDs only, scalar metadata, or vectors. +- `CloudMultiTenantSearchCase`: tenant-routed search for SaaS-shaped workloads. +- `CloudColdLatencyCase`: cold and warm serial latency for first-query and cache-sensitive serving paths. + +The May 2026 release note explains why the Cloud Leaderboard was added, what changed, which systems were tested this round, and how to run each new case: [docs/release/2026-05-cloud-leaderboard.md](docs/release/2026-05-cloud-leaderboard.md). + ### Scoring Rules 1. For each case, select a base value and score each system based on relative values. @@ -721,9 +932,9 @@ Now we can only run one task at the same time. ### Code Structure ![image](https://github.com/zilliztech/VectorDBBench/assets/105927039/8c06512e-5419-4381-b084-9c93aed59639) ### Client -Our client module is designed with flexibility and extensibility in mind, aiming to integrate APIs from different systems seamlessly. As of now, it supports Milvus, Zilliz Cloud, Elastic Search, Pinecone, Qdrant Cloud, Weaviate Cloud, PgVector, Redis, Chroma, CockroachDB, etc. Stay tuned for more options, as we are consistently working on extending our reach to other systems. +Our client module is designed with flexibility and extensibility in mind, aiming to integrate APIs from different systems seamlessly. As of now, it supports Milvus, Zilliz Cloud, Elastic Search, Pinecone, Qdrant Cloud, Weaviate Cloud, PgVector, VectorChord, Redis, Chroma, CockroachDB, VolcMySQL, etc. Stay tuned for more options, as we are consistently working on extending our reach to other systems. ### Benchmark Cases -We've developed lots of comprehensive benchmark cases to test vector databases' various capabilities, each designed to give you a different piece of the puzzle. These cases are categorized into four main types: +We've developed lots of comprehensive benchmark cases to test vector databases' various capabilities, each designed to give you a different piece of the puzzle. These cases are categorized into several main types: #### Capacity Case - **Large Dim:** Tests the database's loading capacity by inserting large-dimension vectors (GIST 100K vectors, 960 dimensions) until fully loaded. The final number of inserted vectors is reported. - **Small Dim:** Similar to the Large Dim case but uses small-dimension vectors (SIFT 500K vectors, 128 dimensions). @@ -735,6 +946,11 @@ We've developed lots of comprehensive benchmark cases to test vector databases' #### Filtering Search Performance Case - **Int-Filter Cases:** Evaluates search performance with int-based filter expression (e.g. "id >= 2,000"). - **Label-Filter Cases:** Evaluates search performance with label-based filter expressions (e.g., "color == 'red'"). The test includes randomly generated labels to simulate real-world filtering scenarios. +#### Full Text Search Performance Case +- **FullTextSearchPerformance:** Measures BM25-style text retrieval over raw text documents. The case inserts documents, runs the backend optimization or index-readiness step, then measures recall, latency, and QPS for text queries. +- **Datasets:** The initial FTS benchmark uses MS MARCO and HotpotQA in small, medium, and large corpus sizes. +- **Ground truth:** Recall, MRR, and NDCG are computed against positive semantic relevance labels from `ir_datasets`. +- **Payload profiles:** FTS supports IDs-only responses and text payload responses so users can compare pure retrieval throughput against response-size overhead. #### Streaming Cases - **Insertion-Under-Load Case:** Evaluates search performance while maintaining a constant insertion workload. VDBBench applies a steady stream of insert requests at a fixed rate to simulate real-world scenarios where search operations must perform reliably under continuous data ingestion. diff --git a/docs/release/2026-05-cloud-leaderboard.md b/docs/release/2026-05-cloud-leaderboard.md new file mode 100644 index 000000000..c9dc76a7b --- /dev/null +++ b/docs/release/2026-05-cloud-leaderboard.md @@ -0,0 +1,170 @@ +# VectorDBBench Cloud Leaderboard Release Note + +May 2026 + +The VectorDBBench Cloud Leaderboard moves beyond a single raw-throughput ranking. It evaluates managed vector databases around the behaviors production teams have to plan for: ingest readiness, payload-aware search, tenant-shaped workloads, cold latency, and cost at practical QPS targets. + +## Why we need a new leaderboard now + +The vector database market has moved past the "highest QPS wins" phase. Production teams choosing a managed vector database also care about budget, data freshness, tail latency, recall, metadata payloads, tenant isolation, and operational predictability. + +The existing VectorDBBench leaderboard remains useful for comparing baseline search performance across systems. But cloud buyers ask a wider set of questions: + +- When does newly inserted data become searchable? +- When is it fully indexed? +- What happens when search returns metadata or vectors instead of only IDs? +- What happens when traffic is split across many tenants? +- What does each reachable QPS tier cost? + +The Cloud Leaderboard is designed around those questions. It keeps performance visible, but puts it next to the readiness, payload, tenant, cold-start, and cost signals that determine what a customer can safely deploy. + +## What the Cloud Leaderboard changes + +The Cloud Leaderboard is a production cloud decision layer, not a replacement for the original raw-performance board. The main change is that benchmark cases now model cloud operating concerns directly instead of treating all products as simple warm search engines. + +The new cases add: + +- Insert readiness measurement, including client insert completion, searchable delay, and indexed delay. +- Explicit response payload profiles: IDs only, scalar label metadata, or vector values. +- Cloud cold-latency measurement for the first search path after idle or cache-cold conditions. +- Multi-tenant search, where data is split into deterministic tenant labels or namespaces and queries are routed by tenant. +- Cost-oriented interpretation, so raw QPS can be read together with monthly cost and readiness constraints. + +This matters because a top-line QPS table can hide important tradeoffs. A system can look strong on peak throughput while losing ground on recall, p99 latency, cold-start behavior, payload cost, or sustained cost at the same target QPS. + +## Who we tested this round + +This round focuses on three popular cloud vector databases: + +- Zilliz Cloud, including tiered and fixed-capacity configurations. +- turbopuffer, including normal, pinned, and backpressure-related configurations where applicable. +- Pinecone serverless. + +The tested matrix is intentionally cloud-oriented. It compares managed products and managed-service modes rather than only local or self-hosted engine behavior. + +## The new tests we added + +Version 2 adds four cloud-oriented cases in VectorDBBench. Each case is designed to expose a production behavior that a plain QPS benchmark can miss. + +### CloudInsertCase + +**Purpose.** CloudInsertCase measures write readiness, not just client-side insert speed. This is important for backfills, migrations, daily refreshes, and release workflows where a team needs to know when newly written vectors can safely take traffic. + +**How it works.** The case loads the dataset with `ConcurrentInsertRunner`, records insert completion time and rows per second, then polls the database until inserted data is fully searchable and fully indexed. The resulting metric separates: + +- `insert_completion_seconds` +- `insert_rows_per_second` +- `searchable_after_insert_seconds` +- `indexed_after_searchable_seconds` + +Example: run LAION 100M insert readiness on Zilliz Cloud with a 10k batch size. + +```bash +vectordbbench zillizautoindex \ + --case-type CloudInsertCase \ + --uri "$ZILLIZ_URI" \ + --token "$ZILLIZ_TOKEN" \ + --collection-name cloud_insert_laion100m_bs10k \ + --cloud-insert-batch-size 10000 \ + --load-concurrency 16 \ + --skip-search-serial \ + --skip-search-concurrent \ + --task-label cloud-insert-zilliz-12cu +``` + +### CloudPayloadSearchCase + +**Purpose.** CloudPayloadSearchCase measures search when the response body resembles production traffic. Many applications return more than vector IDs: they return scalar metadata, labels, or the vector values themselves. That response payload can change throughput, latency, and even product ranking. + +**How it works.** The case extends the normal performance case with an explicit `payload_profile`. Supported profiles are: + +- `ids_only` +- `scalar_label` +- `vector` + +The case can also run unfiltered search, integer-filter search through `--cloud-filter-rate`, or scalar-label filter search through `--cloud-label-percentage`. It records QPS, latency, recall where applicable, and estimated response payload bytes per query. + +When `payload_profile` is `scalar_label`, VectorDBBench materializes scalar label data even for unfiltered runs. This keeps the loaded schema aligned with the requested response payload instead of only loading labels for scalar-label filter runs. + +Example: run vector-payload search on Pinecone with a highly selective integer filter. + +```bash +vectordbbench pinecone \ + --case-type CloudPayloadSearchCase \ + --api-key "$PINECONE_API_KEY" \ + --index-name "$PINECONE_INDEX" \ + --payload-profile vector \ + --cloud-filter-rate 0.001 \ + --k 100 \ + --num-concurrency 60,80 \ + --concurrency-duration 30 \ + --task-label cloud-payload-pinecone-vector-int-filter-0-1p +``` + +### CloudMultiTenantSearchCase + +**Purpose.** CloudMultiTenantSearchCase models SaaS-shaped traffic. Instead of treating the dataset as one flat global collection, it splits records across many tenants and routes each query to a tenant. This highlights products whose namespace, partition-key, or tenant-filter paths behave differently from single-tenant search. + +**How it works.** The case defaults to the Cohere 10M dataset and assigns each row to a deterministic tenant by `row_id % tenant_count`. During search, queries are routed to the corresponding tenant label or namespace. The case supports the same payload profiles and optional filter modes as payload search. + +Tenant routing labels and scalar payload labels are separate concepts. A multi-tenant run can route by tenant while still storing and returning scalar-label payload data when `payload_profile` is `scalar_label`, and scalar-label filters continue to use the scalar label field rather than the tenant routing field. + +TurboPuffer tenant namespace cache warmup is explicit. By default, `CloudMultiTenantSearchCase` does not warm tenant namespaces during `optimize()`; use `--multitenant-warmup-policy all` only when the benchmark should model proactively warmed tenant namespaces. + +Example: run 1,000-tenant IDs-only search on turbopuffer. + +```bash +vectordbbench turbopuffer \ + --case-type CloudMultiTenantSearchCase \ + --dataset-with-size-type "Large Cohere (768dim, 10M)" \ + --api-key "$TURBOPUFFER_API_KEY" \ + --region aws-us-east-1 \ + --namespace vdbbench_mt_seed \ + --multitenant-namespace-prefix vdbbench_mt_ \ + --tenant-count 1000 \ + --tenant-prefix tenant_ \ + --tenant-id-width 4 \ + --payload-profile ids_only \ + --num-concurrency 40,60,80 \ + --concurrency-duration 30 \ + --task-label cloud-multitenant-turbopuffer-1000t +``` + +### CloudColdLatencyCase + +**Purpose.** CloudColdLatencyCase measures first-query and cold-path latency that warm benchmark loops can hide. This matters for serverless products, storage-tiered products, idle workloads, and customer-facing applications where the first query after an idle period is visible to users. + +**How it works.** The case is intentionally search-only and must run against an existing collection that has already become cold according to the product's cache and storage behavior. It rejects `drop_old` and `load` stages because insert-then-immediately-search runs can leave caches, indexing paths, or vendor warmup APIs in an ambiguous state. `ColdWarmSearchRunner` runs serial searches in cold and warm passes. It records cold-latency details in `additional_parameters["cold_latency"]` and also records payload profile and estimated payload bytes per query. + +Example: run a pinned turbopuffer cold-latency test with scalar-label payloads. + +```bash +vectordbbench turbopuffer \ + --case-type CloudColdLatencyCase \ + --skip-drop-old \ + --skip-load \ + --api-key "$TURBOPUFFER_API_KEY" \ + --region aws-us-east-1 \ + --namespace cloud_cold_latency_scalar_label \ + --pin-namespace \ + --pin-replicas 2 \ + --payload-profile scalar_label \ + --cloud-cold-query-count 1000 \ + --skip-search-concurrent \ + --task-label cloud-cold-latency-turbopuffer-pinned-scalar-label +``` + +## Caveats + +This release note introduces the new Cloud Leaderboard direction; it is not the full benchmark report. Detailed tables, raw JSON artifacts, pricing worksheets, and edge-case analysis should live in the benchmark report or external result artifact repository. + +Important caveats: + +- Pricing changes over time. Cost charts need a pricing date, region, and configuration assumptions. +- Managed-service configuration can materially change results, especially for serverless scaling, pinned replicas, capacity units, and storage-tiering modes. +- "Fully indexed" and "fully searchable" readiness may be exposed differently by each vendor, so the implementation must document how each status is detected or inferred. +- The current multi-tenant case uses deterministic tenant assignment and uniform tenant routing. It does not represent every SaaS tenant distribution. +- Multi-tenant routing labels or namespaces are not equivalent to scalar payload labels. Benchmark clients must keep those fields separate when a run combines tenant routing with scalar-label payload or filter behavior. +- Cold latency depends on cache state, idle window, replica pinning, storage architecture, and service warmup behavior. The idle and warmup rules must stay strict between products. +- Payload search rankings are workload-specific. IDs-only, scalar-label, vector-return, integer-filter, and label-filter runs can produce different winners. +- Cost Pareto results must be read together with recall, latency, payload profile, and readiness constraints rather than as a standalone ranking. diff --git a/docs/release/2026-06-full-text-search.md b/docs/release/2026-06-full-text-search.md new file mode 100644 index 000000000..6c7a35a7b --- /dev/null +++ b/docs/release/2026-06-full-text-search.md @@ -0,0 +1,118 @@ +# VectorDBBench Full Text Search Release Note + +June 2026 + +Full text search support adds BM25-style text retrieval workloads to VectorDBBench. The goal is to compare database full text search paths with the same benchmark harness used for vector workloads: repeatable datasets, explicit load/search stages, structured result JSONs, and frontend result visualization. + +## Context + +VectorDBBench has historically focused on dense vector search and vector-oriented cloud cases. That leaves a gap for systems that also expose native text retrieval, sparse search, or BM25 ranking. Users evaluating retrieval systems often need to compare text-only retrieval before deciding whether to use dense vector, sparse vector, hybrid, or reranking layers. + +The Full Text Search benchmark covers that text-only layer. It measures end-to-end behavior for indexing raw text, optimizing the backend, searching with BM25-style ranking, and validating retrieval quality against semantic relevance labels from `ir_datasets`. + +The benchmark also records payload behavior. Some applications return only document IDs, while others return text fields in the search response. VectorDBBench models both paths through explicit payload profiles so throughput and latency can be interpreted together with the response shape. + +## Who we tested this round + +This round focuses on full text search support across the following backends: + +- Milvus, using its full text search BM25 path. +- Zilliz Cloud, using the cloud full text search path. +- Elasticsearch, using its BM25 text search path. +- OSS OpenSearch, using its BM25 text search path. +- Vespa, using BM25 ranking over indexed text fields. +- turbopuffer, using its full text search namespace path. + +The benchmark is designed to keep the workload shape consistent while still recording backend-specific behavior. BM25 parameters and analyzer settings are controlled through the backend case config or CLI flags, so users can run either product-default comparisons or explicitly tuned comparisons. + +## The new tests we added + +### FullTextSearchPerformance + +**Purpose.** FullTextSearchPerformance measures BM25-style full text search as a first-class benchmark case. It answers the baseline question: after a backend indexes the same text corpus, what QPS, latency, recall, MRR, and NDCG does it deliver for text queries? + +**How it works.** The case loads raw text documents, builds the backend text index, runs the backend optimize path, executes optional serial quality checks, and then runs concurrent search. The result metric records load duration, insert duration, optimize duration, QPS, serial latency, concurrent latency, recall, MRR, NDCG, payload profile, inserted count, and explicit backend parameters. + +Example: run MS MARCO small on Milvus with IDs-only responses. + +```bash +vectordbbench milvusfts \ + --case-type FTSBm25Performance \ + --dataset-with-size-type "MS MARCO Small (100K documents)" \ + --uri "$MILVUS_URI" \ + --payload-profile ids_only \ + --load-concurrency 0 \ + --num-concurrency 40,80 \ + --task-label fts-milvus-msmarco-small-ids +``` + +### Full text search datasets and semantic ground truth + +**Purpose.** Full text search quality should measure whether a backend returns documents that are semantically relevant to the query, not only whether it reproduces one particular BM25 implementation's exact ranking. This aligns the benchmark with common IR evaluation practice and makes recall, MRR, and NDCG comparable across backend analyzers and ranking implementations. + +**How it works.** FTS datasets provide raw text for document insertion and query execution through `ir_datasets`. VectorDBBench loads positive qrels from the same source and uses them as semantic ground truth for serial quality checks. BM25 and analyzer settings are not loaded from dataset manifests; use backend config or CLI flags when a run needs explicit `k1`, `b`, analyzer, or backend-specific search parameters. + +Example: use a larger dataset while keeping the same FTS case type. + +```bash +vectordbbench elasticcloudhnsw \ + --case-type FTSBm25Performance \ + --dataset-with-size-type "HotpotQA Medium (1M documents)" \ + --host "$ELASTIC_HOST" \ + --port "$ELASTIC_PORT" \ + --password "$ELASTIC_PASSWORD" \ + --payload-profile ids_only \ + --load-concurrency 0 \ + --num-concurrency 40,80 \ + --task-label fts-elastic-hotpotqa-medium-ids +``` + +### Payload-aware full text search + +**Purpose.** Payload-aware FTS measures the cost of returning text fields, not only document IDs. This matters for applications where search results immediately include snippets, source text, or other fields needed by downstream ranking and display layers. + +**How it works.** The FTS case supports `payload_profile` values including `ids_only` and `text`. IDs-only runs are the recall baseline because they can run serial search against the ground truth without paying text-return overhead. Text payload runs measure concurrent search throughput and latency for larger response bodies; they can skip serial recall when the same indexed namespace or collection has already been validated by the IDs-only run. + +Example: run Vespa with text payload responses and concurrent search only. + +```bash +vectordbbench vespa \ + --case-type FTSBm25Performance \ + --dataset-with-size-type "MS MARCO Medium (1M documents)" \ + --uri "$VESPA_URI" \ + --port "$VESPA_PORT" \ + --payload-profile text \ + --load-concurrency 0 \ + --skip-search-serial \ + --num-concurrency 40,80 \ + --task-label fts-vespa-msmarco-medium-text +``` + +## Result artifacts + +Committed FTS result JSONs live under `vectordb_bench/results/FullTextSearch//`. Published artifacts are consolidated so each backend directory contains one backend-level JSON with multiple case results in its `results` list, instead of a pile of one-case files. That keeps the release payload readable while preserving the per-dataset and per-payload records needed by the dashboard. + +Use the result collector to consolidate split FTS run outputs before committing published examples: + +```bash +python -m vectordb_bench.backend.result_collector \ + vectordb_bench/results/FullTextSearch \ + --merge-by-db \ + --task-label fts_standard \ + --replace +``` + +The default collector behavior still groups normal benchmark files by `run_id`. The `--merge-by-db` mode is intended for curated FTS result publication, where each backend should present the latest benchmark matrix as a single artifact. The Full Text Search frontend reads the latest backend-level result file from each backend directory and displays the committed matrix by backend, dataset, payload profile, load duration, QPS, recall, and latency. + +## Caveats + +This release note introduces the FTS benchmark path; it is not a complete benchmark report. Detailed raw outputs, machine setup, backend deployment scripts, and rerun notes should live in separate experiment reports when needed. + +Important caveats: + +- Semantic relevance labels are not the same as exact BM25 implementation correctness. A high quality score means the backend retrieved judged-relevant documents, not that it matched a specific analyzer or scorer bit-for-bit. +- Analyzer behavior can materially affect recall and ranking. Tokenization, lowercase filters, stop words, stemming, token length limits, and field normalization should be recorded with the benchmark run when they are configured explicitly. +- BM25 parameter support differs across products. Some backends expose `k1` and `b`, some expose average field length controls, and some compute or hide those values internally. Use backend config or CLI flags for explicit comparisons; otherwise the run represents product-default behavior. +- IDs-only and text payload runs answer different questions. IDs-only is the cleanest recall and throughput baseline; text payload runs expose response-size overhead. +- Load duration includes backend-specific insert and optimize behavior. Products may differ in whether optimize means force merge, compaction, warmup, or index deployment readiness. +- Result JSONs under `vectordb_bench/results/FullTextSearch` are curated examples for the frontend. They should not be treated as the full historical experiment archive. diff --git a/install/requirements_py3.11.txt b/install/requirements_py3.11.txt index 5d5702492..a6f3f32cf 100644 --- a/install/requirements_py3.11.txt +++ b/install/requirements_py3.11.txt @@ -5,6 +5,8 @@ pinecone weaviate-client elasticsearch==8.16.0 opensearch-py +boto3 +requests-aws4auth pgvector pgvecto_rs[psycopg3]>=0.2.1 sqlalchemy @@ -14,17 +16,20 @@ pytz streamlit-autorefresh streamlit>=1.23.0 streamlit_extras +tornado>=6.0 tqdm s3fs psutil polars plotly environs -pydantic=2.0,<3 scikit-learn -pymilvus +pymilvus>=2.6.15,<3.0.0 clickhouse_connect pyvespa mysql-connector-python +PyMySQL packaging -hdrhistogram>=0.10.1 \ No newline at end of file +hdrhistogram>=0.10.1 +ir_datasets diff --git a/pyproject.toml b/pyproject.toml index c240ae55a..ddbd3425b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta" [tool.setuptools.packages.find] where = ["."] -include = ["vectordb_bench", "vectordb_bench.cli"] +include = ["vectordb_bench", "vectordb_bench.*"] [project] name = "vectordb-bench" @@ -26,10 +26,10 @@ classifiers = [ ] dependencies = [ "click", + "pyyaml", "pytz", - "streamlit-autorefresh", - "streamlit<1.44,!=1.34.0", # There is a breaking change in 1.44 related to get_page https://discuss.streamlit.io/t/from-streamlit-source-util-import-get-pages-gone-in-v-1-44-0-need-urgent-help/98399 - "streamlit_extras", + "streamlit>=1.61,<2", # Stateful tabs and stretch widths used by the FTS page + "tornado>=6.0", "tqdm", "s3fs", "oss2", @@ -37,11 +37,12 @@ dependencies = [ "polars", "plotly", "environs", - "pydantic>=2.0", + "pydantic>=2.0,<3", "scikit-learn", - "pymilvus", # with pandas, numpy, ujson - "ujson", + "pymilvus>=2.6.15,<3.0.0", # with pandas, numpy "hdrhistogram>=0.10.1", + "ujson", + "ir_datasets", ] dynamic = ["version"] @@ -51,41 +52,11 @@ test = [ "ruff", "pytest", ] -restful = [ "flask" ] - -all = [ - "grpcio==1.53.0", # for qdrant-client and pymilvus - "grpcio-tools==1.53.0", # for qdrant-client and pymilvus - "qdrant-client", - "pinecone", - "weaviate-client", - "elasticsearch", - "sqlalchemy", - "redis", - "chromadb", - "pgvector", - "psycopg", - "psycopg-binary", - "pgvecto_rs[psycopg3]>=0.2.2", - "opensearch-dsl", - "opensearch-py", - "memorydb", - "alibabacloud_ha3engine_vector", - "mariadb", - "PyMySQL", - "clickhouse-connect", - "pyvespa", - "lancedb", - "mysql-connector-python", - "turbopuffer[fast]", - 'zvec', - "endee==0.1.10", # TODO: check pydantic v2 compat -] - +restful = [ "flask" ] qdrant = [ "qdrant-client" ] pinecone = [ "pinecone" ] -weaviate = [ "weaviate-client" ] -elastic = [ "elasticsearch" ] +weaviate = [ "weaviate-client>=3.26.7,<4" ] +elastic = [ "elasticsearch>=8,<9" ] # For elastic and aliyun_elasticsearch pgvector = [ "psycopg", "psycopg-binary", "pgvector" ] @@ -95,7 +66,7 @@ pgvecto_rs = [ "pgvecto_rs[psycopg3]>=0.2.2" ] redis = [ "redis" ] memorydb = [ "memorydb" ] chromadb = [ "chromadb" ] -opensearch = [ "opensearch-py" ] +opensearch = [ "opensearch-py", "boto3", "requests-aws4auth" ] aliyun_opensearch = [ "alibabacloud_ha3engine_vector" ] mongodb = [ "pymongo" ] mariadb = [ "mariadb" ] @@ -105,12 +76,17 @@ clickhouse = [ "clickhouse-connect" ] vespa = [ "pyvespa" ] lancedb = [ "lancedb" ] oceanbase = [ "mysql-connector-python" ] -alisql = [ "mysql-connector-python" ] +alisql = [ "mysqlclient" ] +polardb = [ "PyMySQL" ] doris = [ "doris-vector-search" ] turbopuffer = [ "turbopuffer" ] zvec = [ "zvec" ] endee = [ "endee==0.1.10" ] lindorm = [ "opensearch-py" ] +seekdb = [ "mysql-connector-python" ] +volc_mysql = [ "mysql-connector-python" ] +pinot = [ "requests" ] +adbpg = [ "psycopg", "psycopg-binary", "pgvector" ] antfly = [ "httpx" ] [project.urls] @@ -157,7 +133,7 @@ lint.ignore = [ "INP001", # TODO "TID252", # TODO "N801", "N802", "N815", - "S101", "S108", "S603", "S311", + "S101", "S108", "S603", "S311", "S608", "PLR2004", "RUF017", "C416", @@ -241,4 +217,3 @@ builtins-ignorelist = [ "vectordb_bench/backend/clients/*" = ["PLC0415"] "vectordb_bench/cli/batch_cli.py" = ["PLC0415"] "vectordb_bench/backend/data_source.py" = ["PLC0415"] - diff --git a/tests/pytest.ini b/tests/pytest.ini index ad082137c..9f5751ee3 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -1,4 +1,8 @@ [pytest] -filterwarnings = +filterwarnings = ignore::UserWarning + ignore::DeprecationWarning + +markers = + integration: tests that require external services or network access (deselect with -m "not integration") diff --git a/tests/test_adbpg.py b/tests/test_adbpg.py new file mode 100644 index 000000000..465acf762 --- /dev/null +++ b/tests/test_adbpg.py @@ -0,0 +1,210 @@ +"""Unit tests for the ADB-PG Nova client config layer. + +These tests do not require a live database — they only exercise: + - AdbpgConfig defaults and connection-string assembly + - AdbpgIndexConfig.index_param() WITH-clause options (incl. raw auto_reduction) + - AdbpgIndexConfig.session_param() fastann GUC emission + - TestResult.read_file() round-trip when password is absent in saved JSON + (regression for the result-loading failure caused by polymorphic + serialization stripping subclass fields from DBConfig) + +Usage: + pytest tests/test_adbpg.py -v +""" + +from __future__ import annotations + +import json +from typing import TYPE_CHECKING + +import pytest +from pydantic import SecretStr + +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.adbpg.config import AdbpgConfig, AdbpgIndexConfig +from vectordb_bench.backend.clients.api import MetricType +from vectordb_bench.models import TestResult + +if TYPE_CHECKING: + from pathlib import Path + + +def make_index_config(**overrides) -> AdbpgIndexConfig: + base = { + "metric_type": MetricType.COSINE, + "hnsw_m": 32, + "ef_search": 100, + "ef_construction": 200, + "nlist": 1024, + "algorithm": "novamr", + "rabitq_bits": 7, + "quantize_rescore_amp": 1.0, + "nova_adaptive_gamma": 0.0, + "max_scan_points": 2000, + "index_scan_mode": "snapshot", + "auto_reduction": False, + "nprobe": 5, + } + base.update(overrides) + return AdbpgIndexConfig(**base) + + +class TestAdbpgConfig: + def test_defaults_allow_construction_without_password(self): + # Regression: result JSON only contains DBConfig parent fields + # (db_label/version/note) because of pydantic polymorphic serialization. + # AdbpgConfig must therefore be constructible from that minimal dict. + cfg = AdbpgConfig(db_label="", version="", note="") + assert cfg.host == "localhost" + assert cfg.port == 5432 + assert cfg.db_name == "postgres" + assert cfg.password.get_secret_value() == "" + + def test_to_dict_carries_utility_session_option(self): + cfg = AdbpgConfig( + user_name=SecretStr("u"), + password=SecretStr("pw"), + host="h.example.com", + port=5432, + db_name="postgres", + ) + d = cfg.to_dict() + assert d["table_name"] == "vector" + cc = d["connect_config"] + assert cc["host"] == "h.example.com" + assert cc["user"] == "u" + assert cc["password"] == "pw" # noqa: S105 + assert cc["dbname"] == "postgres" + assert cc["options"] == "-c gp_session_role=utility" + + +class TestAdbpgIndexConfigBuild: + def test_parse_metric(self): + assert make_index_config(metric_type=MetricType.L2).parse_metric() == "l2" + assert make_index_config(metric_type=MetricType.COSINE).parse_metric() == "cosine" + assert make_index_config(metric_type=MetricType.IP).parse_metric() == "ip" + + def test_parse_metric_unsupported_raises(self): + with pytest.raises(ValueError, match="Metric type"): + make_index_config(metric_type=None).parse_metric() + + def test_index_param_options_default(self): + params = make_index_config().index_param() + names = {opt["option_name"]: opt for opt in params["index_creation_with_options"]} + assert names["algorithm"]["val"] == "novamr" + assert names["hnsw_m"]["val"] == 32 + assert names["hnsw_ef_construction"]["val"] == 200 + assert names["nlist"]["val"] == 1024 + assert names["rabitq_bits"]["val"] == 7 + assert names["max_key_len"]["val"] == 1 + # auto_reduction is omitted when False + assert "auto_reduction" not in names + + def test_index_param_auto_reduction_emits_raw(self): + params = make_index_config(auto_reduction=True).index_param() + opt = next(o for o in params["index_creation_with_options"] if o["option_name"] == "auto_reduction") + # `raw=True` so the value is rendered as a bare SQL identifier (`on`) + # rather than a quoted literal. + assert opt["val"] == "on" + assert opt.get("raw") is True + + def test_index_param_pca_dim_omitted_when_none(self): + params = make_index_config(pca_dim=None).index_param() + names = {opt["option_name"] for opt in params["index_creation_with_options"]} + assert "pca_dim" not in names + + def test_index_param_pca_dim_emitted_when_set(self): + params = make_index_config(pca_dim=448).index_param() + opt = next(o for o in params["index_creation_with_options"] if o["option_name"] == "pca_dim") + assert opt["val"] == 448 + + +class TestAdbpgIndexConfigSession: + def test_session_param_emits_all_search_gucs(self): + cfg = make_index_config( + quantize_rescore_amp=0.6, + nova_adaptive_gamma=0.0, + ef_search=50, + max_scan_points=16000, + index_scan_mode="snapshot", + nprobe=64, + ) + opts = cfg.session_param()["session_options"] + emitted = {o["parameter"]["setting_name"]: o["parameter"]["val"] for o in opts} + assert emitted["fastann.quantize_rescore_amp"] == "0.6" + assert emitted["fastann.nova_adaptive_gamma"] == "0.0" + assert emitted["fastann.hnsw_ef_search"] == "50" + assert emitted["fastann.hnsw_max_scan_points"] == "16000" + assert emitted["fastann.index_scan_mode"] == "snapshot" + # novad-specific GUC is always emitted (no-op for HNSW algorithms) + assert emitted["fastann.nova_nprobe"] == "64" + + def test_session_param_emits_zero_values(self): + # Forcing 0 / 0.0 must still produce a SET command — callers rely on + # being able to pin a GUC to zero. + cfg = make_index_config(quantize_rescore_amp=0.0, nova_adaptive_gamma=0.0, nprobe=0) + opts = cfg.session_param()["session_options"] + emitted = {o["parameter"]["setting_name"]: o["parameter"]["val"] for o in opts} + assert emitted["fastann.quantize_rescore_amp"] == "0.0" + assert emitted["fastann.nova_adaptive_gamma"] == "0.0" + assert emitted["fastann.nova_nprobe"] == "0" + + +class TestResultRoundTrip: + def test_read_file_with_minimal_db_config(self, tmp_path: Path): + """Saved result JSON keeps only DBConfig parent fields for db_config. + + TestResult.read_file must still rehydrate the AdbpgConfig instance + without raising a Field-required pydantic ValidationError. + """ + result_dir = tmp_path / "AnalyticDB for PostgreSQL" + result_dir.mkdir() + result_file = result_dir / "result_test_run.json" + payload = { + "run_id": "round-trip", + "task_label": "round-trip", + "results": [ + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 1.0, + "serial_latency_p99": 0.0, + "serial_latency_p95": 0.0, + "recall": 1.0, + "ndcg": 1.0, + "conc_num_list": [], + "conc_qps_list": [], + "conc_latency_p99_list": [], + "conc_latency_p95_list": [], + "conc_latency_avg_list": [], + }, + "task_config": { + "db": DB.Adbpg.value, + "db_config": {"db_label": "", "version": "", "note": ""}, + "db_case_config": { + "metric_type": "COSINE", + "algorithm": "novamr", + "hnsw_m": 16, + "ef_search": 100, + "ef_construction": 200, + "nlist": 1024, + }, + "case_config": {"case_id": 5, "custom_case": {}, "k": 10}, + "stages": ["search_serial"], + "load_concurrency": 0, + }, + "label": ":)", + } + ], + "timestamp": 0.0, + } + result_file.write_text(json.dumps(payload)) + + tr = TestResult.read_file(result_file, trans_unit=False) + assert len(tr.results) == 1 + rehydrated = tr.results[0].task_config.db_config + assert isinstance(rehydrated, AdbpgConfig) + assert rehydrated.host == "localhost" # came from default diff --git a/tests/test_antfly.py b/tests/test_antfly.py new file mode 100644 index 000000000..024a3b14f --- /dev/null +++ b/tests/test_antfly.py @@ -0,0 +1,107 @@ +import base64 +import struct +from typing import Any + +import pytest + +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.antfly.antfly import Antfly +from vectordb_bench.backend.clients.antfly.config import AntflyConfig, AntflyIndexConfig +from vectordb_bench.backend.filter import IntFilter, LabelFilter, non_filter +from vectordb_bench.backend.payload import PayloadProfile + + +class _CaseConfig: + def index_param(self): + return {"distance_metric": "cosine"} + + def search_param(self): + return {"search_effort": 0.6} + + +class _Response: + is_success = True + status_code = 200 + text = "" + + def raise_for_status(self): + return None + + +class _Client: + def __init__(self): + self.posts = [] + + def post(self, path: str, *, json: dict[str, Any]): + self.posts.append((path, json)) + return _Response() + + +def _adapter(*, with_scalar_labels: bool = False): + adapter = Antfly.__new__(Antfly) + adapter.case_config = _CaseConfig() + adapter.collection_name = "vdbbench" + adapter.with_scalar_labels = with_scalar_labels + adapter._filter_query = None + adapter._pack_query_vectors = True + adapter._legacy_api = False + adapter._write_sync_level = "write" + adapter._bench_status_last_log = 0.0 + adapter.client = _Client() + return adapter + + +def test_antfly_is_registered(): + assert DB.Antfly.config_cls is AntflyConfig + assert DB.Antfly.case_config_cls() is AntflyIndexConfig + assert DB.Antfly.init_cls is Antfly + + +def test_antfly_uses_native_cosine_and_ids_only_queries(): + adapter = _adapter() + + assert adapter.need_normalize_cosine() is False + body = adapter._metadata_query_body([1.0, 2.0], 10) + assert body["fields"] == [] + assert body["search_effort"] == 0.6 + + adapter.prepare_filter(IntFilter(filter_rate=0.99, int_field="id", int_value=49_500)) + assert adapter._metadata_query_body([1.0, 2.0], 10)["filter_query"] == { + "numeric_range": {"field": "id", "min": 49_500, "inclusive_min": True} + } + + adapter.prepare_filter(LabelFilter(label_percentage=0.01)) + assert adapter._filter_query == {"term": {"labels": "label_1p"}} + adapter.prepare_filter(non_filter) + assert "filter_query" not in adapter._metadata_query_body([1.0, 2.0], 10) + + with pytest.raises(NotImplementedError): + adapter.search_embedding([1.0, 2.0], payload_profile=PayloadProfile.VECTOR) + + +def test_antfly_insert_preserves_vectors_and_labels(): + adapter = _adapter(with_scalar_labels=True) + + inserted, error = adapter.insert_embeddings([[3.0, 4.0]], [7], labels_data=["bucket_007"]) + + assert error is None + assert inserted == 1 + _, payload = adapter.client.posts[0] + row = payload["inserts"]["key:7"] + vector = base64.b64decode(row["_embeddings"]["vec"]) + assert struct.unpack("<2f", vector) == pytest.approx((3.0, 4.0)) + assert row["labels"] == "bucket_007" + assert payload["sync_level"] == "write" + + +def test_write_readiness_probe_does_not_create_a_tombstone(): + adapter = _adapter() + client = _Client() + + adapter._wait_for_write_ready(client) + + assert len(client.posts) == 1 + assert client.posts[0][1] == { + "inserts": {"key:__circus_write_probe__": {"id": -1}}, + "sync_level": "write", + } diff --git a/tests/test_aws_opensearch.py b/tests/test_aws_opensearch.py new file mode 100644 index 000000000..2e2930338 --- /dev/null +++ b/tests/test_aws_opensearch.py @@ -0,0 +1,46 @@ +from types import SimpleNamespace + +import pytest + +from vectordb_bench import config +from vectordb_bench.backend.clients.aws_opensearch.aws_opensearch import AWSOpenSearch + + +def test_serverless_insert_uses_configured_batch_size(monkeypatch) -> None: + bulk_requests = [] + + def bulk(*, body): + bulk_requests.append(body) + + monkeypatch.setattr(config, "NUM_PER_BATCH", 2) + + db = object.__new__(AWSOpenSearch) + db.client = SimpleNamespace(bulk=bulk) + db._is_serverless = True + db.index_name = "test-index" + db.vector_col_name = "embedding" + db.with_scalar_labels = False + + inserted, error = db._insert_with_single_client( + embeddings=[[0.1], [0.2], [0.3], [0.4], [0.5]], + metadata=[1, 2, 3, 4, 5], + ) + + assert inserted == 5 + assert error is None + assert [len(request) // 2 for request in bulk_requests] == [2, 2, 1] + assert [document["id"] for request in bulk_requests for document in request[1::2]] == [1, 2, 3, 4, 5] + + +@pytest.mark.parametrize("batch_size", [0, -1]) +def test_serverless_insert_rejects_non_positive_batch_size(monkeypatch, batch_size: int) -> None: + monkeypatch.setattr(config, "NUM_PER_BATCH", batch_size) + + db = object.__new__(AWSOpenSearch) + db._is_serverless = True + + with pytest.raises(ValueError, match="NUM_PER_BATCH must be greater than 0"): + db._insert_with_single_client( + embeddings=[[0.1]], + metadata=[1], + ) diff --git a/tests/test_bench_runner.py b/tests/test_bench_runner.py index e65c73887..7aff0c27e 100644 --- a/tests/test_bench_runner.py +++ b/tests/test_bench_runner.py @@ -1,5 +1,7 @@ import time import logging + +import ujson from vectordb_bench.interface import BenchMarkRunner from vectordb_bench.models import ( DB, IndexType, CaseType, TaskConfig, CaseConfig, @@ -52,9 +54,8 @@ def test_performance_case_no_error(self): ) t = task_config.copy() - d = t.model_dump_json(exclude={'db_config': {'password', 'api_key'}}) + d = t.json(exclude={'db_config': {'password', 'api_key'}}) log.info(f"{d}") - import ujson loads = ujson.loads(d) log.info(f"{loads}") diff --git a/tests/test_case_runner_reuse.py b/tests/test_case_runner_reuse.py new file mode 100644 index 000000000..55dfbda0b --- /dev/null +++ b/tests/test_case_runner_reuse.py @@ -0,0 +1,171 @@ +from pydantic import SecretStr + +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.api import EmptyDBCaseConfig, MetricType +from vectordb_bench.backend.clients.doris.config import DorisCaseConfig, DorisConfig +from vectordb_bench.backend.clients.pinecone.config import PineconeConfig +from vectordb_bench.backend.clients.turbopuffer.config import TurboPufferConfig, TurboPufferIndexConfig +from vectordb_bench.backend.data_source import DatasetSource +from vectordb_bench.backend.dataset import DatasetWithSizeType +from vectordb_bench.backend.task_runner import CaseRunner, RunningStatus, TaskRunner +from vectordb_bench.interface import BenchMarkRunner +from vectordb_bench.metric import Metric +from vectordb_bench.models import CaseConfig, CaseType, TaskConfig, TaskStage, TestResult + + +def make_runner( + *, + case_id: CaseType = CaseType.Performance1536D50K, + custom_case: dict | None = None, + db: DB = DB.TurboPuffer, + db_config=None, + db_case_config=None, + stages: list[TaskStage] | None = None, +) -> CaseRunner: + if db_config is None: + if db == DB.TurboPuffer: + db_config = TurboPufferConfig(api_key="key", region="aws-us-east-1") + elif db == DB.Pinecone: + db_config = PineconeConfig(api_key="key", index_name="idx") + elif db == DB.Doris: + db_config = DorisConfig(password=SecretStr("")) + else: + db_config = DB.Test.config_cls() + if db_case_config is None: + if db == DB.TurboPuffer: + db_case_config = TurboPufferIndexConfig(metric_type=MetricType.COSINE) + elif db == DB.Doris: + db_case_config = DorisCaseConfig(metric_type=MetricType.COSINE) + else: + db_case_config = EmptyDBCaseConfig() + + task = TaskConfig( + db=db, + db_config=db_config, + db_case_config=db_case_config, + case_config=CaseConfig(case_id=case_id, custom_case=custom_case or {}), + stages=stages or [TaskStage.DROP_OLD, TaskStage.LOAD, TaskStage.SEARCH_SERIAL], + ) + return CaseRunner( + run_id="run-id", + config=task, + ca=task.case_config.case, + status=RunningStatus.PENDING, + dataset_source=DatasetSource.S3, + ) + + +def assert_not_reusable(left: CaseRunner, right: CaseRunner) -> None: + assert left != right + assert right != left + assert hash(left) != hash(right) + + +def test_reuse_key_distinguishes_multitenant_routing_parameters(): + base_case = { + "dataset_with_size_type": DatasetWithSizeType.CohereSmall.value, + "tenant_count": 2, + "tenant_prefix": "tenant_", + "tenant_id_width": 4, + } + + assert_not_reusable( + make_runner(case_id=CaseType.CloudMultiTenantSearchCase, custom_case=base_case), + make_runner(case_id=CaseType.CloudMultiTenantSearchCase, custom_case={**base_case, "tenant_count": 3}), + ) + assert_not_reusable( + make_runner(case_id=CaseType.CloudMultiTenantSearchCase, custom_case=base_case), + make_runner(case_id=CaseType.CloudMultiTenantSearchCase, custom_case={**base_case, "tenant_prefix": "org_"}), + ) + assert_not_reusable( + make_runner(case_id=CaseType.CloudMultiTenantSearchCase, custom_case=base_case), + make_runner(case_id=CaseType.CloudMultiTenantSearchCase, custom_case={**base_case, "tenant_id_width": 2}), + ) + + +def test_reuse_key_distinguishes_single_layout_from_multitenant_layout(): + dataset_case = {"dataset_with_size_type": DatasetWithSizeType.CohereSmall.value} + + assert_not_reusable( + make_runner(case_id=CaseType.CloudPayloadSearchCase, custom_case=dataset_case), + make_runner( + case_id=CaseType.CloudMultiTenantSearchCase, + custom_case={**dataset_case, "tenant_count": 2}, + ), + ) + + +def test_reuse_key_preserves_safe_payload_reuse(): + dataset_case = {"dataset_with_size_type": DatasetWithSizeType.CohereSmall.value} + + ids_only = make_runner( + case_id=CaseType.CloudPayloadSearchCase, + custom_case={**dataset_case, "payload_profile": "ids_only"}, + ) + vector = make_runner( + case_id=CaseType.CloudPayloadSearchCase, + custom_case={**dataset_case, "payload_profile": "vector"}, + ) + + assert ids_only == vector + assert hash(ids_only) == hash(vector) + + +def test_reuse_key_distinguishes_physical_db_targets(): + assert_not_reusable( + make_runner(db_config=TurboPufferConfig(api_key="key", region="aws-us-east-1", namespace="namespace_a")), + make_runner(db_config=TurboPufferConfig(api_key="key", region="aws-us-east-1", namespace="namespace_b")), + ) + assert_not_reusable( + make_runner(db=DB.Pinecone, db_config=PineconeConfig(api_key="key", index_name="index_a")), + make_runner(db=DB.Pinecone, db_config=PineconeConfig(api_key="key", index_name="index_b")), + ) + + +def test_reuse_key_distinguishes_doris_case_derived_table_names(): + assert_not_reusable( + make_runner(db=DB.Doris, case_id=CaseType.Performance768D1M), + make_runner( + db=DB.Doris, + case_id=CaseType.NewIntFilterPerformanceCase, + custom_case={ + "dataset_with_size_type": DatasetWithSizeType.CohereMedium.value, + "filter_rate": 0.01, + }, + ), + ) + + +def test_search_only_runner_does_not_suppress_later_full_load(monkeypatch): + calls: list[bool] = [] + search_only = make_runner( + stages=[TaskStage.SEARCH_SERIAL], + ) + full_load = make_runner( + stages=[TaskStage.DROP_OLD, TaskStage.LOAD, TaskStage.SEARCH_SERIAL], + ) + + def fake_run(self: CaseRunner, drop_old: bool = True) -> Metric: + calls.append(drop_old) + return Metric() + + class SendConn: + def __init__(self): + self.sent = [] + + def send(self, value): + self.sent.append(value) + + def close(self): + return None + + monkeypatch.setattr(CaseRunner, "run", fake_run) + monkeypatch.setattr(TestResult, "display", lambda self: None) + monkeypatch.setattr(TestResult, "flush", lambda self: None) + + BenchMarkRunner()._async_task_v2( + TaskRunner(run_id="run-id", task_label="task", case_runners=[search_only, full_load]), + SendConn(), + ) + + assert calls == [False, True] diff --git a/tests/test_cli_note.py b/tests/test_cli_note.py new file mode 100644 index 000000000..1c601686d --- /dev/null +++ b/tests/test_cli_note.py @@ -0,0 +1,125 @@ +import time +from pathlib import Path + +from click.testing import CliRunner +from pytest import MonkeyPatch + +from vectordb_bench.backend.clients.endee import cli as endee_cli +from vectordb_bench.backend.clients.test import cli as test_cli +from vectordb_bench.cli import cli as common_cli + + +def invoke_test_command(monkeypatch: MonkeyPatch, args: list[str]): + captured = {} + + def fake_run(tasks, task_label): + captured["task"] = tasks[0] + captured["task_label"] = task_label + + monkeypatch.setattr(common_cli.benchmark_runner, "run", fake_run) + monkeypatch.setattr(common_cli.benchmark_runner, "has_running", lambda: False) + result = CliRunner().invoke(test_cli.Test, args) + return result, captured + + +def invoke_endee_command(monkeypatch: MonkeyPatch, args: list[str]): + captured = {} + + def fake_run(tasks, task_label): + captured["task"] = tasks[0] + captured["task_label"] = task_label + + monkeypatch.setattr(endee_cli.benchmark_runner, "run", fake_run) + monkeypatch.setattr(endee_cli.benchmark_runner, "has_running", lambda: False) + monkeypatch.setattr(time, "sleep", lambda _: None) + result = CliRunner().invoke( + endee_cli.Endee, + ["--token", "secret", "--index-name", "test-index", *args], + ) + return result, captured + + +def test_common_cli_exposes_note_options() -> None: + result = CliRunner().invoke(test_cli.Test, ["--help"]) + + assert result.exit_code == 0, result.output + assert "--note TEXT" in result.output + assert "--note-file FILE" in result.output + + +def test_common_cli_stores_inline_note_in_task_config(monkeypatch: MonkeyPatch) -> None: + note = '{"schema":"vdbbench-context/v1","deployment":"local"}' + + result, captured = invoke_test_command(monkeypatch, ["--note", note]) + + assert result.exit_code == 0, result.output + assert captured["task"].db_config.note == note + + +def test_common_cli_reads_note_file_into_task_config(monkeypatch: MonkeyPatch, tmp_path: Path) -> None: + note = '{\n "schema": "vdbbench-context/v1",\n "deployment": "managed"\n}' + note_file = tmp_path / "run-context.json" + note_file.write_text(note + "\n", encoding="utf-8") + + result, captured = invoke_test_command(monkeypatch, ["--note-file", str(note_file)]) + + assert result.exit_code == 0, result.output + assert captured["task"].db_config.note == note + + +def test_endee_cli_reads_note_file_into_task_config(monkeypatch: MonkeyPatch, tmp_path: Path) -> None: + note = '{"schema":"vdbbench-context/v1","deployment":"endee-local"}' + note_file = tmp_path / "endee-run-context.json" + note_file.write_text(note, encoding="utf-8") + + result, captured = invoke_endee_command(monkeypatch, ["--note-file", str(note_file)]) + + assert result.exit_code == 0, result.output + assert captured["task"].db_config.note == note + + +def test_common_cli_rejects_inline_note_with_note_file(monkeypatch: MonkeyPatch, tmp_path: Path) -> None: + note_file = tmp_path / "run-context.json" + note_file.write_text("context", encoding="utf-8") + + result, _ = invoke_test_command( + monkeypatch, + ["--note", "inline", "--note-file", str(note_file)], + ) + + assert result.exit_code != 0 + assert "--note and --note-file cannot be used together" in result.output + + +def test_common_cli_rejects_empty_inline_note_with_note_file( + monkeypatch: MonkeyPatch, + tmp_path: Path, +) -> None: + note_file = tmp_path / "run-context.json" + note_file.write_text("context", encoding="utf-8") + + result, _ = invoke_test_command(monkeypatch, ["--note", "", "--note-file", str(note_file)]) + + assert result.exit_code != 0 + assert "--note and --note-file cannot be used together" in result.output + + +def test_common_cli_rejects_empty_note_file(monkeypatch: MonkeyPatch, tmp_path: Path) -> None: + note_file = tmp_path / "empty.txt" + note_file.write_text("", encoding="utf-8") + + result, _ = invoke_test_command(monkeypatch, ["--note-file", str(note_file)]) + + assert result.exit_code != 0 + assert "Note file is empty" in result.output + + +def test_common_cli_rejects_non_utf8_note_file(monkeypatch: MonkeyPatch, tmp_path: Path) -> None: + note_file = tmp_path / "invalid.txt" + with note_file.open("wb") as output: + output.write(b"\xff") + + result, _ = invoke_test_command(monkeypatch, ["--note-file", str(note_file)]) + + assert result.exit_code != 0 + assert "Note file is not valid UTF-8" in result.output diff --git a/tests/test_cloud_cold_latency_case.py b/tests/test_cloud_cold_latency_case.py new file mode 100644 index 000000000..55f610b38 --- /dev/null +++ b/tests/test_cloud_cold_latency_case.py @@ -0,0 +1,470 @@ +import json +from contextlib import contextmanager +from pathlib import Path + +import numpy as np +import pytest + +from vectordb_bench.backend.assembler import Assembler +from vectordb_bench.backend.cases import CaseLabel, CaseType, CloudColdLatencyCase +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.api import EmptyDBCaseConfig +from vectordb_bench.backend.clients.pinecone.config import PineconeConfig +from vectordb_bench.backend.data_source import DatasetSource +from vectordb_bench.backend.dataset import DatasetWithSizeType +from vectordb_bench.backend.filter import Filter, FilterOp +from vectordb_bench.backend.payload import PayloadProfile +from vectordb_bench.backend.result_collector import ResultCollector +from vectordb_bench.backend.runner.cold_warm_runner import ColdWarmSearchRunner +from vectordb_bench.backend.task_runner import CaseRunner, RunningStatus +from vectordb_bench.cli.cli import get_custom_case_config +from vectordb_bench.metric import Metric +from vectordb_bench.models import CaseConfig, CaseResult, TaskConfig, TaskStage, TestResult + + +def test_cloud_cold_latency_case_defaults_to_laion_100m(): + case = CloudColdLatencyCase() + + assert case.case_id == CaseType.CloudColdLatencyCase + assert case.label == CaseLabel.CloudColdLatency + assert case.dataset.data.name == "LAION" + assert case.dataset.data.size == 100_000_000 + assert case.dataset.data.dim == 768 + assert case.payload_profile == PayloadProfile.IDS_ONLY + assert case.query_count == 1000 + assert case.filters.type == FilterOp.NonFilter + + +def test_cloud_cold_latency_case_accepts_payload_dataset_and_int_filter(): + case = CloudColdLatencyCase( + dataset_with_size_type=DatasetWithSizeType.CohereSmall.value, + payload_profile="vector", + filter_rate=0.9, + query_count=10, + ) + + assert case.dataset_with_size_type == DatasetWithSizeType.CohereSmall + assert case.dataset.data.name == "Cohere" + assert case.payload_profile == PayloadProfile.VECTOR + assert case.filter_rate == 0.9 + assert case.query_count == 10 + assert case.filters.type == FilterOp.NumGE + + +def test_cloud_cold_latency_case_accepts_label_filter(): + case = CloudColdLatencyCase(label_percentage=0.9) + + assert case.label_percentage == 0.9 + assert case.filters.type == FilterOp.StrEqual + + +def test_cloud_cold_latency_case_rejects_two_filter_types(): + with pytest.raises(ValueError, match="supports only one filter type"): + CloudColdLatencyCase(filter_rate=0.9, label_percentage=0.9) + + +def test_cloud_cold_latency_case_rejects_invalid_query_count(): + with pytest.raises(ValueError, match="query_count must be positive"): + CloudColdLatencyCase(query_count=0) + + +def test_case_config_builds_cloud_cold_latency_case_from_custom_case(): + case = CaseConfig( + case_id=CaseType.CloudColdLatencyCase, + custom_case={ + "payload_profile": "scalar_label", + "label_percentage": 0.9, + "query_count": 12, + }, + ).case + + assert isinstance(case, CloudColdLatencyCase) + assert case.payload_profile == PayloadProfile.SCALAR_LABEL + assert case.label_percentage == 0.9 + assert case.query_count == 12 + + +def test_cli_builds_cloud_cold_latency_custom_case_config(): + params = { + "case_type": "CloudColdLatencyCase", + "payload_profile": "vector", + "cloud_filter_rate": 0.9, + "cloud_label_percentage": None, + "cloud_cold_query_count": 1000, + "dataset_with_size_type": DatasetWithSizeType.CohereMedium.value, + } + + assert get_custom_case_config(params) == { + "payload_profile": "vector", + "filter_rate": 0.9, + "query_count": 1000, + "dataset_with_size_type": DatasetWithSizeType.CohereMedium.value, + } + + +def test_cli_keeps_cloud_cold_latency_default_dataset_as_laion(): + params = { + "case_type": "CloudColdLatencyCase", + "payload_profile": "ids_only", + "cloud_filter_rate": None, + "cloud_label_percentage": None, + "cloud_cold_query_count": 1000, + "dataset_with_size_type": None, + } + + custom_case = get_custom_case_config(params) + case = CaseConfig(case_id=CaseType.CloudColdLatencyCase, custom_case=custom_case).case + + assert custom_case == { + "payload_profile": "ids_only", + "query_count": 1000, + } + assert case.dataset.data.name == "LAION" + assert case.dataset.data.size == 100_000_000 + + +def test_cloud_cold_latency_result_file_uses_cold_latency_metrics(tmp_path: Path): + cold_latency = { + "cold_stats": { + "first_query_latency": 0.2, + "p99_latency": 0.3, + "p95_latency": 0.25, + "avg_latency": 0.21, + }, + "warm_stats": { + "first_query_latency": 0.1, + "p99_latency": 0.15, + "p95_latency": 0.12, + "avg_latency": 0.11, + }, + "ratios": { + "first_query_latency": 2.0, + "p99_latency": 2.0, + "p95_latency": 2.0833, + "avg_latency": 1.9091, + }, + } + result = CaseResult( + task_config=TaskConfig( + db=DB.Pinecone, + db_config=PineconeConfig( + db_label="pinecone_cloud_cold_latency", + api_key="secret-key", + index_name="laion100m", + ), + db_case_config=EmptyDBCaseConfig(), + case_config=CaseConfig( + case_id=CaseType.CloudColdLatencyCase, + custom_case={"payload_profile": "vector", "query_count": 1000}, + ), + stages=[TaskStage.SEARCH_SERIAL], + load_concurrency=0, + ), + metrics=Metric( + insert_duration=0.0, + optimize_duration=0.0, + load_duration=0.0, + payload_profile="vector", + payload_estimated_bytes_per_query=309200, + additional_parameters={"cold_latency": cold_latency}, + ), + ) + test_result = TestResult(run_id="run-id", task_label="cloud_cold_latency_pinecone", results=[result]) + + test_result.write_db_file(tmp_path, test_result, "pinecone") + + result_file = next(tmp_path.glob("result_*_pinecone.json")) + raw_output = result_file.read_text() + assert raw_output.startswith('{\n "run_id"') + written = json.loads(raw_output) + assert written["results"][0]["metrics"] == { + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "payload_profile": "vector", + "payload_estimated_bytes_per_query": 309200, + "cold_latency": cold_latency, + } + assert written["results"][0]["task_config"]["db_config"]["api_key"] == "**********" + assert written["results"][0]["task_config"]["db_config"]["index_name"] == "laion100m" + assert written["results"][0]["task_config"]["case_config"] == { + "case_id": 700, + "custom_case": {"payload_profile": "vector", "query_count": 1000}, + } + + read_back = TestResult.read_file(result_file) + assert read_back.results[0].task_config.case_config.case_id == CaseType.CloudColdLatencyCase + assert read_back.results[0].task_config.case_config.custom_case == { + "payload_profile": "vector", + "query_count": 1000, + } + assert read_back.results[0].metrics.additional_parameters["cold_latency"] == cold_latency + + collected = ResultCollector.collect(tmp_path) + assert len(collected) == 1 + assert collected[0].results[0].metrics.additional_parameters["cold_latency"] == cold_latency + + +class FakeColdWarmDB: + name = "FakeColdWarmDB" + + def __init__(self, supported_payload_profiles: set[PayloadProfile] | None = None): + self.supported_payload_profiles = supported_payload_profiles or {PayloadProfile.IDS_ONLY} + self.calls = [] + self.prepare_filter_calls = [] + self.init_enter_count = 0 + + def supports_payload_profile(self, payload_profile: PayloadProfile) -> bool: + return payload_profile in self.supported_payload_profiles + + def need_normalize_cosine(self) -> bool: + return False + + @contextmanager + def init(self): + self.init_enter_count += 1 + yield + + def prepare_filter(self, filters: Filter): + self.prepare_filter_calls.append(filters) + + def search_embedding(self, query: list[float], k: int = 100, **kwargs) -> list[int]: + self.calls.append((query, k, kwargs)) + return list(range(k)) + + +def test_cold_warm_runner_computes_stats_and_ratios(monkeypatch: pytest.MonkeyPatch): + db = FakeColdWarmDB() + # Cold latencies: 0.2, 0.2, 0.2. Warm latencies: 0.1, 0.1, 0.1. + perf_values = iter([0.0, 0.2, 0.2, 0.4, 0.4, 0.6, 0.6, 0.7, 0.7, 0.8, 0.8, 0.9]) + monkeypatch.setattr("vectordb_bench.backend.runner.cold_warm_runner.time.perf_counter", lambda: next(perf_values)) + + runner = ColdWarmSearchRunner( + db=db, + test_data=[[0.1], [0.2], [0.3]], + k=3, + query_count=3, + ) + + result = runner.run() + + assert result == { + "cold_stats": { + "first_query_latency": 0.2, + "p99_latency": 0.2, + "p95_latency": 0.2, + "avg_latency": 0.2, + }, + "warm_stats": { + "first_query_latency": 0.1, + "p99_latency": 0.1, + "p95_latency": 0.1, + "avg_latency": 0.1, + }, + "cold_warm_ratio": { + "first_query_latency_ratio": 2.0, + "p99_latency_ratio": 2.0, + "p95_latency_ratio": 2.0, + "avg_latency_ratio": 2.0, + }, + } + assert db.init_enter_count == 1 + assert len(db.prepare_filter_calls) == 1 + assert [call[0] for call in db.calls] == [[0.1], [0.2], [0.3], [0.1], [0.2], [0.3]] + + +def test_cold_warm_runner_passes_payload_profile_in_both_passes(monkeypatch: pytest.MonkeyPatch): + db = FakeColdWarmDB(supported_payload_profiles={PayloadProfile.IDS_ONLY, PayloadProfile.VECTOR}) + perf_values = iter([0.0, 0.1, 0.1, 0.2]) + monkeypatch.setattr("vectordb_bench.backend.runner.cold_warm_runner.time.perf_counter", lambda: next(perf_values)) + + runner = ColdWarmSearchRunner( + db=db, + test_data=[np.array([0.1])], + k=3, + payload_profile=PayloadProfile.VECTOR, + query_count=1, + ) + + runner.run() + + assert db.calls == [ + ([0.1], 3, {"payload_profile": PayloadProfile.VECTOR}), + ([0.1], 3, {"payload_profile": PayloadProfile.VECTOR}), + ] + + +def test_cold_warm_runner_omits_payload_profile_for_ids_only(monkeypatch: pytest.MonkeyPatch): + db = FakeColdWarmDB() + perf_values = iter([0.0, 0.1, 0.1, 0.2]) + monkeypatch.setattr("vectordb_bench.backend.runner.cold_warm_runner.time.perf_counter", lambda: next(perf_values)) + + runner = ColdWarmSearchRunner( + db=db, + test_data=[[0.1]], + k=3, + payload_profile=PayloadProfile.IDS_ONLY, + query_count=1, + ) + + runner.run() + + assert db.calls == [ + ([0.1], 3, {}), + ([0.1], 3, {}), + ] + + +def test_cold_warm_runner_fails_for_unsupported_payload_profile(): + db = FakeColdWarmDB() + + with pytest.raises(NotImplementedError, match="payload_profile=vector"): + ColdWarmSearchRunner( + db=db, + test_data=[[0.1]], + payload_profile=PayloadProfile.VECTOR, + query_count=1, + ) + + +def test_cold_warm_runner_rejects_too_few_queries(): + db = FakeColdWarmDB() + + with pytest.raises(ValueError, match="query_count=2 exceeds test_data size=1"): + ColdWarmSearchRunner(db=db, test_data=[[0.1]], query_count=2) + + +def test_assembler_schedules_cloud_cold_latency_case(): + task = TaskConfig( + db=DB.Test, + db_config=DB.Test.config_cls(), + db_case_config=EmptyDBCaseConfig(), + case_config=CaseConfig( + case_id=CaseType.CloudColdLatencyCase, + custom_case={"query_count": 1}, + ), + stages=[TaskStage.SEARCH_SERIAL], + ) + + runner = Assembler.assemble_all("run-id", "task-label", [task], DatasetSource.S3) + + assert len(runner.case_runners) == 1 + assert runner.case_runners[0].ca.label == CaseLabel.CloudColdLatency + + +def test_case_runner_stores_cloud_cold_latency_metric(monkeypatch: pytest.MonkeyPatch): + case = CloudColdLatencyCase(query_count=1) + case.dataset.test_data = [[0.1]] + task = TaskConfig( + db=DB.Test, + db_config=DB.Test.config_cls(), + db_case_config=EmptyDBCaseConfig(), + case_config=CaseConfig( + case_id=CaseType.CloudColdLatencyCase, + custom_case={"query_count": 1}, + ), + stages=[TaskStage.SEARCH_SERIAL], + ) + runner = CaseRunner( + run_id="run-id", + config=task, + ca=case, + status=RunningStatus.PENDING, + dataset_source=DatasetSource.S3, + ) + runner.db = FakeColdWarmDB() + + expected = { + "cold_stats": { + "first_query_latency": 0.2, + "p99_latency": 0.2, + "p95_latency": 0.2, + "avg_latency": 0.2, + }, + "warm_stats": { + "first_query_latency": 0.1, + "p99_latency": 0.1, + "p95_latency": 0.1, + "avg_latency": 0.1, + }, + "cold_warm_ratio": { + "first_query_latency_ratio": 2.0, + "p99_latency_ratio": 2.0, + "p95_latency_ratio": 2.0, + "avg_latency_ratio": 2.0, + }, + } + captured_kwargs = {} + + class FakeRunner: + def __init__(self, **kwargs): + captured_kwargs.update(kwargs) + + def run(self): + return expected + + monkeypatch.setattr("vectordb_bench.backend.task_runner.ColdWarmSearchRunner", FakeRunner) + + metric = runner._run_cloud_cold_latency_case(drop_old=False) + + assert metric.additional_parameters["cold_latency"] == expected + assert metric.payload_profile == "ids_only" + assert metric.payload_estimated_bytes_per_query == case.estimated_payload_bytes_per_query(task.case_config.k) + assert captured_kwargs == { + "db": runner.db, + "test_data": [[0.1]], + "filters": case.filters, + "k": task.case_config.k, + "payload_profile": case.payload_profile, + "query_count": case.query_count, + } + + +def test_cloud_cold_latency_case_rejects_drop_old(): + case = CloudColdLatencyCase(query_count=1) + case.dataset.test_data = [[0.1]] + task = TaskConfig( + db=DB.Test, + db_config=DB.Test.config_cls(), + db_case_config=EmptyDBCaseConfig(), + case_config=CaseConfig( + case_id=CaseType.CloudColdLatencyCase, + custom_case={"query_count": 1}, + ), + stages=[TaskStage.SEARCH_SERIAL], + ) + runner = CaseRunner( + run_id="run-id", + config=task, + ca=case, + status=RunningStatus.PENDING, + dataset_source=DatasetSource.S3, + ) + + with pytest.raises(ValueError, match="requires an existing cold collection"): + runner._run_cloud_cold_latency_case(drop_old=True) + + +def test_cloud_cold_latency_case_rejects_load_stage(): + case = CloudColdLatencyCase(query_count=1) + case.dataset.test_data = [[0.1]] + task = TaskConfig( + db=DB.Test, + db_config=DB.Test.config_cls(), + db_case_config=EmptyDBCaseConfig(), + case_config=CaseConfig( + case_id=CaseType.CloudColdLatencyCase, + custom_case={"query_count": 1}, + ), + stages=[TaskStage.LOAD, TaskStage.SEARCH_SERIAL], + ) + runner = CaseRunner( + run_id="run-id", + config=task, + ca=case, + status=RunningStatus.PENDING, + dataset_source=DatasetSource.S3, + ) + + with pytest.raises(ValueError, match="search-only"): + runner._run_cloud_cold_latency_case(drop_old=False) diff --git a/tests/test_cloud_insert_case.py b/tests/test_cloud_insert_case.py new file mode 100644 index 000000000..28e70f7bd --- /dev/null +++ b/tests/test_cloud_insert_case.py @@ -0,0 +1,1108 @@ +import json +from contextlib import contextmanager +from pathlib import Path +from unittest.mock import MagicMock + +import numpy as np +import pandas as pd +import pytest + +from vectordb_bench import config +from vectordb_bench.backend.assembler import Assembler +from vectordb_bench.backend.cases import CaseLabel, CaseType, CloudInsertCase +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.api import EmptyDBCaseConfig, VectorDB +from vectordb_bench.backend.clients.milvus.milvus import Milvus +from vectordb_bench.backend.clients.pinecone.config import PineconeConfig +from vectordb_bench.backend.clients.pinecone.pinecone import Pinecone +from vectordb_bench.backend.clients.turbopuffer.config import TurboPufferIndexConfig +from vectordb_bench.backend.clients.turbopuffer.turbopuffer import TurboPuffer +from vectordb_bench.backend.clients.zilliz_cloud.config import AutoIndexConfig, ZillizCloudConfig +from vectordb_bench.backend.data_source import DatasetSource +from vectordb_bench.backend.dataset import DatasetWithSizeType +from vectordb_bench.backend.payload import PayloadProfile +from vectordb_bench.backend.result_collector import ResultCollector +from vectordb_bench.backend.runner.concurrent_runner import ConcurrentInsertRunner +from vectordb_bench.backend.runner.executor import TaskResult +from vectordb_bench.backend.task_runner import CaseRunner +from vectordb_bench.cli.cli import get_custom_case_config +from vectordb_bench.metric import Metric +from vectordb_bench.models import CaseConfig, CaseResult, TaskConfig, TaskStage, TestResult + + +class _SerialTaskExecutor: + def __init__(self): + self.submitted = [] + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + return False + + def submit(self, fn): + self.submitted.append(fn) + + def wait_all(self): + results = [] + for fn in self.submitted: + try: + results.append(TaskResult(value=fn())) + except Exception as e: + results.append(TaskResult(error=e)) + return results + + +class _ConcurrentRunnerData: + train_id_field = "id" + train_vector_field = "vector" + + +class _ConcurrentRunnerDataset: + data = _ConcurrentRunnerData() + + def __init__(self, ids): + self.ids = ids + + def iter_batches(self, batch_size): + return iter([pd.DataFrame({"id": [row_id], "vector": [np.array([row_id / 10 + 0.1])]}) for row_id in self.ids]) + + +def test_cloud_insert_case_defaults_to_laion_100m(): + case = CloudInsertCase(batch_size=1000) + + assert case.case_id == CaseType.CloudInsertCase + assert case.label == CaseLabel.CloudInsert + assert case.dataset.data.name == "LAION" + assert case.dataset.data.size == 100_000_000 + assert case.batch_size == 1000 + assert case.duration is None + assert case.readiness_timeout is None + + +def test_case_config_builds_cloud_insert_case_from_custom_case(): + case = CaseConfig( + case_id=CaseType.CloudInsertCase, + custom_case={ + "batch_size": 5000, + "duration": 1800, + "dataset_with_size_type": DatasetWithSizeType.CohereMedium.value, + }, + ).case + + assert isinstance(case, CloudInsertCase) + assert case.batch_size == 5000 + assert case.duration == 1800 + assert case.dataset.data.name == "Cohere" + assert case.dataset.data.size == 1_000_000 + + +def test_case_config_builds_cloud_insert_case_from_laion_100m_dataset_option(): + case = CaseConfig( + case_id=CaseType.CloudInsertCase, + custom_case={ + "batch_size": 10_000, + "dataset_with_size_type": "Large LAION (768dim, 100M)", + }, + ).case + + assert isinstance(case, CloudInsertCase) + assert case.batch_size == 10_000 + assert case.dataset_with_size_type == DatasetWithSizeType.LAIONLarge + assert case.dataset.data.name == "LAION" + assert case.dataset.data.size == 100_000_000 + assert case.dataset.data.dim == 768 + + +def test_laion_100m_dataset_option_uses_100m_timeouts(): + assert DatasetWithSizeType.LAIONLarge.get_load_timeout() == config.LOAD_TIMEOUT_768D_100M + assert DatasetWithSizeType.LAIONLarge.get_optimize_timeout() == config.OPTIMIZE_TIMEOUT_768D_100M + + +def test_cli_builds_cloud_insert_custom_case_config(): + params = { + "case_type": "CloudInsertCase", + "cloud_insert_batch_size": 10_000, + "cloud_insert_duration": 1800, + "cloud_insert_readiness_timeout": 7200, + "cloud_insert_readiness_poll_interval": 10, + "dataset_with_size_type": DatasetWithSizeType.CohereMedium.value, + } + + assert get_custom_case_config(params) == { + "batch_size": 10_000, + "duration": 1800, + "readiness_timeout": 7200, + "readiness_poll_interval": 10, + "dataset_with_size_type": DatasetWithSizeType.CohereMedium.value, + } + + +def test_cli_builds_cloud_insert_custom_case_config_with_laion_100m_dataset(): + cfg = get_custom_case_config( + { + "case_type": "CloudInsertCase", + "cloud_insert_batch_size": 10_000, + "cloud_insert_duration": None, + "cloud_insert_readiness_timeout": None, + "cloud_insert_readiness_poll_interval": None, + "dataset_with_size_type": DatasetWithSizeType.LAIONLarge.value, + } + ) + + assert cfg == { + "batch_size": 10_000, + "duration": None, + "dataset_with_size_type": DatasetWithSizeType.LAIONLarge.value, + } + + case = CaseConfig(case_id=CaseType.CloudInsertCase, custom_case=cfg).case + assert case.dataset_with_size_type == DatasetWithSizeType.LAIONLarge + assert case.dataset.data.name == "LAION" + assert case.dataset.data.size == 100_000_000 + + +def test_cli_builds_cloud_insert_custom_case_config_with_default_dataset(): + cfg = get_custom_case_config( + { + "case_type": "CloudInsertCase", + "cloud_insert_batch_size": 10_000, + "cloud_insert_duration": None, + "cloud_insert_readiness_timeout": None, + "cloud_insert_readiness_poll_interval": None, + "dataset_with_size_type": None, + } + ) + + assert cfg == { + "batch_size": 10_000, + "duration": None, + "dataset_with_size_type": DatasetWithSizeType.CohereMedium.value, + } + + case = CaseConfig(case_id=CaseType.CloudInsertCase, custom_case=cfg).case + assert case.dataset_with_size_type == DatasetWithSizeType.CohereMedium + assert case.dataset.data.size == 1_000_000 + + +def test_cli_builds_multitenant_custom_case_config(): + cfg = get_custom_case_config( + { + "case_type": "CloudMultiTenantSearchCase", + "dataset_with_size_type": "Small Cohere (768dim, 100K)", + "tenant_count": 13, + "tenant_prefix": "acct_", + "tenant_id_width": 3, + "payload_profile": "vector", + "cloud_filter_rate": 0.01, + "cloud_label_percentage": None, + } + ) + + assert cfg == { + "dataset_with_size_type": "Small Cohere (768dim, 100K)", + "tenant_count": 13, + "tenant_prefix": "acct_", + "tenant_id_width": 3, + "payload_profile": "vector", + "filter_rate": 0.01, + } + + +def test_cli_omits_multitenant_dataset_when_not_selected(): + cfg = get_custom_case_config( + { + "case_type": "CloudMultiTenantSearchCase", + "dataset_with_size_type": None, + "tenant_count": 13, + "tenant_prefix": "acct_", + "tenant_id_width": 3, + "payload_profile": "ids_only", + "cloud_filter_rate": None, + "cloud_label_percentage": None, + } + ) + + assert cfg == { + "tenant_count": 13, + "tenant_prefix": "acct_", + "tenant_id_width": 3, + "payload_profile": "ids_only", + } + + case = CaseConfig(case_id=CaseType.CloudMultiTenantSearchCase, custom_case=cfg).case + assert case.dataset_with_size_type == DatasetWithSizeType.CohereLarge + assert case.dataset.data.size == 10_000_000 + + +def test_assembler_schedules_cloud_insert_case(): + task = TaskConfig( + db=DB.ZillizCloud, + db_config=ZillizCloudConfig(uri="https://example.com", user="db_admin"), + db_case_config=AutoIndexConfig(), + case_config=CaseConfig( + case_id=CaseType.CloudInsertCase, + custom_case={ + "batch_size": 1000, + "dataset_with_size_type": DatasetWithSizeType.CohereMedium.value, + }, + ), + stages=[TaskStage.DROP_OLD, TaskStage.LOAD], + ) + + runner = Assembler.assemble_all("run-id", "task-label", [task], DatasetSource.S3) + + assert len(runner.case_runners) == 1 + assert runner.case_runners[0].ca.label == CaseLabel.CloudInsert + + +def test_default_insert_readiness_is_immediately_ready(): + class FakeVectorDB(VectorDB): + def __init__(self, dim, db_config, db_case_config, collection_name="", drop_old=False): + pass + + @contextmanager + def init(self): + yield + + def insert_embeddings(self, embeddings, metadata, labels_data=None, **kwargs): + return len(embeddings), None + + def search_embedding(self, query, k=100, payload_profile=None): + return [] + + def optimize(self, data_size=None): + pass + + assert FakeVectorDB(1, {}, EmptyDBCaseConfig()).poll_insert_readiness(10) == { + "fully_searchable": True, + "fully_indexed": True, + "additional_parameters": {}, + } + + +def test_metric_contains_cloud_insert_output_fields(): + metric = Metric( + inserted_count=100, + insert_rows_per_second=83.33, + insert_completion_seconds=1.2, + searchable_after_insert_seconds=3.4, + indexed_after_searchable_seconds=5.6, + additional_parameters={"disable_backpressure": True}, + ) + + assert metric.inserted_count == 100 + assert metric.insert_rows_per_second == 83.33 + assert metric.insert_completion_seconds == 1.2 + assert metric.searchable_after_insert_seconds == 3.4 + assert metric.indexed_after_searchable_seconds == 5.6 + assert metric.additional_parameters == {"disable_backpressure": True} + + +def test_cloud_insert_result_file_uses_insert_only_metrics(tmp_path: Path): + result = CaseResult( + task_config=TaskConfig( + db=DB.Pinecone, + db_config=PineconeConfig( + db_label="pinecone_cloud_insert_laion100m_bs1k", + api_key="secret-key", + index_name="laion100m", + ), + db_case_config=EmptyDBCaseConfig(), + case_config=CaseConfig( + case_id=CaseType.CloudInsertCase, + custom_case={"batch_size": 1000, "duration": None}, + ), + stages=[TaskStage.DROP_OLD, TaskStage.LOAD], + load_concurrency=0, + ), + metrics=Metric( + inserted_count=100_000_000, + insert_rows_per_second=3919.9296, + insert_completion_seconds=255.1066, + searchable_after_insert_seconds=0.0, + indexed_after_searchable_seconds=28.5956, + additional_parameters={}, + ), + ) + test_result = TestResult(run_id="run-id", task_label="cloud_insert_pinecone_laion100m_bs1k", results=[result]) + + test_result.write_db_file(tmp_path, test_result, "pinecone") + + result_file = next(tmp_path.glob("result_*_pinecone.json")) + raw_output = result_file.read_text() + assert raw_output.startswith('{\n "run_id"') + written = json.loads(raw_output) + assert written["results"][0]["metrics"] == { + "inserted_count": 100_000_000, + "insert_rows_per_second": 3919.9296, + "insert_completion_seconds": 255.1066, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 28.5956, + "additional_parameters": {}, + } + assert written["results"][0]["task_config"]["db_config"]["api_key"] == "**********" + assert written["results"][0]["task_config"]["db_config"]["index_name"] == "laion100m" + assert written["results"][0]["task_config"]["case_config"] == { + "case_id": 600, + "custom_case": {"batch_size": 1000, "duration": None}, + } + + read_back = TestResult.read_file(result_file) + assert read_back.results[0].task_config.case_config.case_id == CaseType.CloudInsertCase + assert read_back.results[0].task_config.case_config.custom_case == {"batch_size": 1000, "duration": None} + + collected = ResultCollector.collect(tmp_path) + assert len(collected) == 1 + assert collected[0].results[0].metrics.inserted_count == 100_000_000 + + +def test_turbopuffer_insert_can_disable_backpressure(): + db = TurboPuffer.__new__(TurboPuffer) + db.with_scalar_labels = False + db._scalar_id_field = "id" + db._vector_field = "vector" + db.metric = "cosine_distance" + db.db_case_config = TurboPufferIndexConfig(disable_backpressure=True) + + class Namespace: + kwargs = None + + def write(self, **kwargs): + self.kwargs = kwargs + + db.ns = Namespace() + + assert db.insert_embeddings([[0.1]], [1]) == (1, None) + assert db.ns.kwargs["disable_backpressure"] is True + + +def test_turbopuffer_insert_serializes_numpy_vectors(): + db = TurboPuffer.__new__(TurboPuffer) + db.with_scalar_labels = False + db._scalar_id_field = "id" + db._vector_field = "vector" + db.metric = "cosine_distance" + db.db_case_config = TurboPufferIndexConfig(disable_backpressure=False) + + class Namespace: + kwargs = None + + def write(self, **kwargs): + self.kwargs = kwargs + + db.ns = Namespace() + + insert_count, error = db.insert_embeddings([np.array([0.1, 0.2])], [1]) + + assert error is None + assert insert_count == 1 + assert db.ns.kwargs["upsert_columns"]["vector"] == [[0.1, 0.2]] + + +def test_turbopuffer_insert_returns_write_error(): + db = TurboPuffer.__new__(TurboPuffer) + db.with_scalar_labels = False + db._scalar_id_field = "id" + db._vector_field = "vector" + db.metric = "cosine_distance" + db.db_case_config = TurboPufferIndexConfig(disable_backpressure=False) + + class Namespace: + def write(self, **kwargs): + raise RuntimeError("write failed") + + db.ns = Namespace() + + insert_count, error = db.insert_embeddings([[0.1]], [1]) + + assert insert_count == 0 + assert isinstance(error, RuntimeError) + + +def test_milvus_insert_readiness_uses_entity_count_and_index_progress(): + db = Milvus.__new__(Milvus) + db.collection_name = "c" + db._vector_index_name = "vector_idx" + db.client = type( + "Client", + (), + { + "flush": lambda self, collection_name: None, + "get_collection_stats": lambda self, collection_name: {"row_count": "10"}, + "describe_index": lambda self, collection_name, index_name: {"pending_index_rows": 0}, + }, + )() + + assert db.poll_insert_readiness(10) == { + "fully_searchable": True, + "fully_indexed": True, + "additional_parameters": {}, + } + + +def test_pinecone_insert_readiness_uses_vector_count(): + db = Pinecone.__new__(Pinecone) + db.index = type("Index", (), {"describe_index_stats": lambda self: {"total_vector_count": 9}})() + + assert db.poll_insert_readiness(10)["fully_searchable"] is False + assert db.poll_insert_readiness(9)["fully_indexed"] is True + + +def test_pinecone_supports_cloud_payload_profiles(): + db = Pinecone.__new__(Pinecone) + + assert db.supports_payload_profile(PayloadProfile.IDS_ONLY) + assert db.supports_payload_profile(PayloadProfile.SCALAR_LABEL) + assert db.supports_payload_profile(PayloadProfile.VECTOR) + + +def test_pinecone_search_requests_metadata_for_scalar_label_payload(): + db = Pinecone.__new__(Pinecone) + db.expr = None + + class Index: + kwargs = None + + def query(self, **kwargs): + self.kwargs = kwargs + return {"matches": [{"id": "1"}]} + + db.index = Index() + + assert db.search_embedding([0.1], payload_profile=PayloadProfile.SCALAR_LABEL) == [1] + assert db.index.kwargs["include_metadata"] is True + assert db.index.kwargs["include_values"] is False + + +def test_pinecone_search_requests_values_for_vector_payload(): + db = Pinecone.__new__(Pinecone) + db.expr = {"meta": {"$gte": 10}} + + class Index: + kwargs = None + + def query(self, **kwargs): + self.kwargs = kwargs + return {"matches": [{"id": "2"}]} + + db.index = Index() + + assert db.search_embedding([0.1], payload_profile=PayloadProfile.VECTOR) == [2] + assert db.index.kwargs["include_metadata"] is False + assert db.index.kwargs["include_values"] is True + assert db.index.kwargs["filter"] == {"meta": {"$gte": 10}} + + +def test_pinecone_search_retries_rate_limited_queries(monkeypatch): + db = Pinecone.__new__(Pinecone) + db.expr = None + monkeypatch.setenv("PINECONE_QUERY_RETRY_SLEEP_SECONDS", "0.25") + sleeps = [] + monkeypatch.setattr("vectordb_bench.backend.clients.pinecone.pinecone.time.sleep", sleeps.append) + + class RateLimitError(Exception): + status = 429 + + class Index: + calls = 0 + + def query(self, **kwargs): + self.calls += 1 + if self.calls < 3: + raise RateLimitError("too many requests") + return {"matches": [{"id": "3"}]} + + db.index = Index() + + assert db.search_embedding([0.1]) == [3] + assert db.index.calls == 3 + assert sleeps == [0.25, 0.25] + + +def test_pinecone_search_stops_after_rate_limit_retry_budget(monkeypatch): + db = Pinecone.__new__(Pinecone) + db.expr = None + monkeypatch.setenv("PINECONE_QUERY_MAX_RETRIES", "1") + monkeypatch.setattr("vectordb_bench.backend.clients.pinecone.pinecone.time.sleep", lambda _: None) + + class RateLimitError(Exception): + status = 429 + + class Index: + def query(self, **kwargs): + raise RateLimitError("too many requests") + + db.index = Index() + + try: + db.search_embedding([0.1]) + except RateLimitError: + pass + else: + raise AssertionError("expected Pinecone rate limit error") + + +def test_pinecone_insert_tracks_last_write_lsn(): + db = Pinecone.__new__(Pinecone) + db.batch_size = 1000 + db.with_scalar_labels = False + db._scalar_id_field = "meta" + + class UpsertResponse: + _response_info = {"raw_headers": {"x-pinecone-request-lsn": "123"}} + + class Index: + def upsert(self, records): + return UpsertResponse() + + db.index = Index() + + insert_count, error = db.insert_embeddings([[0.1]], [1]) + + assert error is None + assert insert_count == 1 + assert db._last_write_lsn == 123 + + +def test_pinecone_insert_keeps_highest_write_lsn(): + db = Pinecone.__new__(Pinecone) + db.batch_size = 1 + db.with_scalar_labels = False + db._scalar_id_field = "meta" + responses = iter(["123", "122"]) + + class UpsertResponse: + def __init__(self, lsn): + self._response_info = {"raw_headers": {"x-pinecone-request-lsn": lsn}} + + class Index: + def upsert(self, records): + return UpsertResponse(next(responses)) + + db.index = Index() + + insert_count, error = db.insert_embeddings([[0.1], [0.2]], [1, 2]) + + assert error is None + assert insert_count == 2 + assert db._last_write_lsn == 123 + + +def test_pinecone_record_write_lsn_keeps_highest_value(): + db = Pinecone.__new__(Pinecone) + + db._record_write_lsn(123) + db._record_write_lsn(122) + + assert db._last_write_lsn == 123 + + +def test_pinecone_insert_readiness_uses_lsn_when_available(): + db = Pinecone.__new__(Pinecone) + db._last_write_lsn = 123 + db._readiness_probe_vector = [0.0] + + class QueryResponse(dict): + def __init__(self, indexed_lsn): + super().__init__({"matches": []}) + self._response_info = {"raw_headers": {"x-pinecone-max-indexed-lsn": str(indexed_lsn)}} + + class Index: + indexed_lsn = 122 + + def describe_index_stats(self): + return {"total_vector_count": 10} + + def query(self, **kwargs): + return QueryResponse(self.indexed_lsn) + + db.index = Index() + + assert db.poll_insert_readiness(10)["fully_searchable"] is False + db.index.indexed_lsn = 123 + assert db.poll_insert_readiness(10)["fully_indexed"] is True + + +def test_turbopuffer_insert_readiness_uses_unindexed_bytes(): + db = TurboPuffer.__new__(TurboPuffer) + db.db_case_config = TurboPufferIndexConfig(disable_backpressure=True) + db.ns = type("Namespace", (), {"metadata": lambda self: {"unindexed_bytes": 1}})() + + status = db.poll_insert_readiness(10) + + assert status["fully_searchable"] is True + assert status["fully_indexed"] is False + assert status["additional_parameters"] == {"disable_backpressure": True} + + +def test_turbopuffer_insert_readiness_uses_nested_unindexed_bytes(): + db = TurboPuffer.__new__(TurboPuffer) + db.db_case_config = TurboPufferIndexConfig(disable_backpressure=False) + db.ns = type( + "Namespace", + (), + {"metadata": lambda self: {"index": {"status": "updating", "unindexed_bytes": 1}}}, + )() + + status = db.poll_insert_readiness(10) + + assert status["fully_searchable"] is True + assert status["fully_indexed"] is False + assert status["additional_parameters"] == {"disable_backpressure": False} + + +def test_cloud_insert_runner_records_insert_and_readiness_metrics(monkeypatch): + class Data: + train_id_field = "id" + train_vector_field = "vector" + metric_type = "L2" + + class Dataset: + data = Data() + + def iter_batches(self, batch_size): + assert batch_size == 2 + return iter( + [ + pd.DataFrame({"id": [1, 2], "vector": [np.array([0.1]), np.array([0.2])]}), + pd.DataFrame({"id": [3], "vector": [np.array([0.3])]}), + ] + ) + + class DB: + thread_safe = True + name = "fake" + inserts = [] + readiness_calls = 0 + + @contextmanager + def init(self): + yield + + def need_normalize_cosine(self): + return False + + def insert_embeddings(self, embeddings, metadata, labels_data=None): + self.inserts.append((embeddings, metadata)) + return len(metadata), None + + def poll_insert_readiness(self, expected_count): + self.readiness_calls += 1 + return { + "fully_searchable": self.readiness_calls >= 2, + "fully_indexed": self.readiness_calls >= 3, + "additional_parameters": {"example": "value"}, + } + + db = DB() + monkeypatch.setattr("vectordb_bench.backend.task_runner.time.sleep", lambda _: None) + case = CloudInsertCase(batch_size=2) + case.dataset = Dataset() + config = type("Config", (), {"load_concurrency": 1})() + runner = CaseRunner.construct(ca=case, db=db, config=config) + + metric = runner._run_cloud_insert_case() + + assert [metadata for _, metadata in db.inserts] == [[1, 2], [3]] + assert metric.inserted_count == 3 + assert metric.insert_rows_per_second > 0 + assert metric.insert_completion_seconds >= 0 + assert metric.searchable_after_insert_seconds >= 0 + assert metric.indexed_after_searchable_seconds >= 0 + assert metric.additional_parameters == {"example": "value"} + + +def test_cloud_insert_runner_times_out_when_readiness_never_completes(monkeypatch): + class Data: + train_id_field = "id" + train_vector_field = "vector" + metric_type = "L2" + + class Dataset: + data = Data() + + def iter_batches(self, batch_size): + return iter([pd.DataFrame({"id": [1], "vector": [np.array([0.1])]})]) + + class DB: + @contextmanager + def init(self): + yield + + def need_normalize_cosine(self): + return False + + def poll_insert_readiness(self, expected_count): + return { + "fully_searchable": False, + "fully_indexed": False, + "additional_parameters": {"reason": "stalled"}, + } + + class FakeConcurrentInsertRunner: + def __init__(self, *args, **kwargs): + pass + + def task(self): + return 1 + + def fail_on_sleep(_seconds): + raise AssertionError("readiness polling did not time out before sleeping") + + monkeypatch.setattr("vectordb_bench.backend.task_runner.ConcurrentInsertRunner", FakeConcurrentInsertRunner) + monkeypatch.setattr("vectordb_bench.backend.task_runner.time.sleep", fail_on_sleep) + case = CloudInsertCase(batch_size=1, readiness_timeout=0, readiness_poll_interval=0) + case.dataset = Dataset() + runner = CaseRunner.construct(ca=case, db=DB(), config=type("Config", (), {"load_concurrency": 1})()) + + with pytest.raises(TimeoutError, match="fully_searchable.*last_status.*stalled"): + runner._run_cloud_insert_case() + + +def test_cloud_insert_runner_uses_concurrent_insert_runner(monkeypatch): + created = {} + + class Data: + metric_type = "L2" + + class Dataset: + data = Data() + + class FakeConcurrentInsertRunner: + def __init__(self, db, dataset, normalize, filters, max_workers, batch_size, duration): + created.update( + { + "db": db, + "dataset": dataset, + "normalize": normalize, + "filters": filters, + "max_workers": max_workers, + "batch_size": batch_size, + "duration": duration, + } + ) + + def task(self): + return 3 + + class DB: + @contextmanager + def init(self): + yield + + def need_normalize_cosine(self): + return False + + def poll_insert_readiness(self, expected_count): + return {"fully_searchable": True, "fully_indexed": True, "additional_parameters": {}} + + monkeypatch.setattr("vectordb_bench.backend.task_runner.ConcurrentInsertRunner", FakeConcurrentInsertRunner) + case = CloudInsertCase(batch_size=1000, duration=60) + case.dataset = Dataset() + config = type("Config", (), {"load_concurrency": 7})() + runner = CaseRunner.construct(ca=case, db=DB(), config=config) + + metric = runner._run_cloud_insert_case() + + assert metric.inserted_count == 3 + assert created["batch_size"] == 1000 + assert created["duration"] == 60 + assert created["max_workers"] == 7 + assert created["dataset"] is case.dataset + + +def test_concurrent_insert_runner_does_not_retry_non_retryable_insert_errors(monkeypatch): + from vectordb_bench.backend.runner import concurrent_runner as concurrent_runner_module + + class NonRetryableInsertError(RuntimeError): + non_retryable = True + + class FakeDB: + def __init__(self): + self.calls = 0 + + def insert_embeddings(self, **kwargs): + self.calls += 1 + return 2, NonRetryableInsertError("partial tenant insert") + + monkeypatch.setattr(concurrent_runner_module.time, "sleep", lambda _seconds: None) + + runner = ConcurrentInsertRunner.__new__(ConcurrentInsertRunner) + db = FakeDB() + + with pytest.raises(RuntimeError, match="Non-retryable insert failure"): + runner._insert_batch_with_retry( + db, + embeddings=[[1.0], [2.0], [3.0]], + metadata=[0, 1, 2], + tenant_labels_data=["tenant_0000", "tenant_0001", "tenant_0000"], + ) + + assert db.calls == 1 + + +def test_concurrent_insert_runner_stops_handing_out_batches_after_non_retryable_error(): + class NonRetryableInsertError(RuntimeError): + non_retryable = True + + class DB: + thread_safe = True + name = "fake" + + def __init__(self): + self.calls = [] + + @contextmanager + def init(self): + yield + + def insert_embeddings(self, embeddings, metadata, labels_data=None): + self.calls.append(metadata) + if len(self.calls) == 1: + return 0, NonRetryableInsertError("partial tenant insert") + return len(metadata), None + + db = DB() + runner = ConcurrentInsertRunner( + db, _ConcurrentRunnerDataset([0, 1, 2]), normalize=False, max_workers=2, batch_size=1 + ) + runner._create_executor = lambda: _SerialTaskExecutor() + + with pytest.raises(RuntimeError, match="Non-retryable insert failure"): + runner.task() + + assert db.calls == [[0]] + + +def test_concurrent_insert_runner_stops_handing_out_batches_after_retry_exhaustion(monkeypatch): + from vectordb_bench.backend.runner import concurrent_runner as concurrent_runner_module + + class DB: + thread_safe = True + name = "fake" + + def __init__(self): + self.calls = [] + + @contextmanager + def init(self): + yield + + def insert_embeddings(self, embeddings, metadata, labels_data=None): + self.calls.append(metadata) + return 0, RuntimeError("insert failed") + + monkeypatch.setattr(concurrent_runner_module.config, "MAX_INSERT_RETRY", 0) + + db = DB() + runner = ConcurrentInsertRunner(db, _ConcurrentRunnerDataset([0, 1]), normalize=False, max_workers=2, batch_size=1) + runner._create_executor = lambda: _SerialTaskExecutor() + + with pytest.raises(RuntimeError, match="Insert failed and retried more than 0 times"): + runner.task() + + assert db.calls == [[0]] + + +def test_concurrent_insert_runner_uses_custom_batch_size_iterator(): + class Data: + train_id_field = "id" + train_vector_field = "vector" + + class Dataset: + data = Data() + requested_batch_size = None + + def iter_batches(self, batch_size): + self.requested_batch_size = batch_size + return iter( + [ + pd.DataFrame( + { + "id": [1, 2], + "vector": [np.array([0.1]), np.array([0.2])], + } + ) + ] + ) + + def __iter__(self): + raise AssertionError("ConcurrentInsertRunner should request an explicit batch size") + + class DB: + thread_safe = True + name = "fake" + + def __init__(self): + self.inserts = [] + + @contextmanager + def init(self): + yield + + def insert_embeddings(self, embeddings, metadata, labels_data=None): + self.inserts.append((embeddings, metadata)) + return len(metadata), None + + dataset = Dataset() + db = DB() + runner = ConcurrentInsertRunner(db, dataset, normalize=False, max_workers=1, batch_size=1000) + + assert runner.task() == 2 + assert dataset.requested_batch_size == 1000 + assert db.inserts == [([[0.1], [0.2]], [1, 2])] + + +class TenantInsertProbeDB: + name = "TenantInsertProbeDB" + thread_safe = True + + def __init__(self): + self.calls = [] + + @contextmanager + def init(self): + yield + + def insert_embeddings(self, embeddings, metadata, labels_data=None, tenant_labels_data=None): + self.calls.append( + { + "embeddings": embeddings, + "metadata": metadata, + "labels_data": labels_data, + "tenant_labels_data": tenant_labels_data, + } + ) + return len(embeddings), None + + +class TenantAwareCase: + is_multitenant = True + + def tenant_labels_for_ids(self, row_ids): + return [f"tenant_{int(row_id) % 3:04d}" for row_id in row_ids] + + +def test_concurrent_insert_runner_passes_tenant_labels(): + db = TenantInsertProbeDB() + dataset = MagicMock() + dataset.data.train_id_field = "id" + dataset.data.train_vector_field = "emb" + dataset.iter_batches.return_value = iter( + [ + pd.DataFrame( + { + "id": [0, 1, 5], + "emb": [[1.0, 0.0], [0.0, 1.0], [0.5, 0.5]], + } + ) + ] + ) + + runner = ConcurrentInsertRunner( + db=db, + dataset=dataset, + normalize=False, + max_workers=1, + batch_size=10, + tenant_case=TenantAwareCase(), + ) + + count = runner.task() + + assert count == 3 + assert db.calls[0]["metadata"] == [0, 1, 5] + assert db.calls[0]["tenant_labels_data"] == ["tenant_0000", "tenant_0001", "tenant_0002"] + + +def test_concurrent_insert_runner_passes_scalar_labels_for_scalar_payload_without_filter(): + db = TenantInsertProbeDB() + dataset = MagicMock() + dataset.data.train_id_field = "id" + dataset.data.train_vector_field = "emb" + dataset.data.scalar_labels_file_separated = False + dataset.iter_batches.return_value = iter( + [ + pd.DataFrame( + { + "id": [0, 1, 5], + "emb": [[1.0, 0.0], [0.0, 1.0], [0.5, 0.5]], + "labels": ["label_a", "label_b", "label_c"], + } + ) + ] + ) + + runner = ConcurrentInsertRunner( + db=db, + dataset=dataset, + normalize=False, + max_workers=1, + batch_size=10, + with_scalar_labels=True, + ) + + count = runner.task() + + assert count == 3 + assert db.calls[0]["metadata"] == [0, 1, 5] + assert db.calls[0]["labels_data"] == ["label_a", "label_b", "label_c"] + assert db.calls[0]["tenant_labels_data"] is None + + +def test_pre_run_prepares_separated_scalar_labels_for_scalar_payload(): + class Dataset: + def __init__(self): + self.prepare_kwargs = None + self.data = type("Data", (), {"dim": 2, "metric_type": "L2"})() + + def prepare(self, source, filters, with_train_files, with_scalar_labels=False): + self.prepare_kwargs = { + "source": source, + "filters": filters, + "with_train_files": with_train_files, + "with_scalar_labels": with_scalar_labels, + } + return True + + class Case: + is_multitenant = False + with_scalar_labels = True + filters = MagicMock() + dataset = Dataset() + + class DB: + init_args = None + + @classmethod + def init_cls(cls, **kwargs): + cls.init_args = kwargs + return cls() + + def need_normalize_cosine(self): + return False + + class DBConfig: + def to_dict(self): + return {} + + config = type( + "Config", + (), + { + "db": DB, + "db_config": DBConfig(), + "db_case_config": EmptyDBCaseConfig(), + "stages": [TaskStage.LOAD], + }, + )() + runner = CaseRunner.construct( + ca=Case(), + config=config, + dataset_source=DatasetSource.S3, + ) + + runner._pre_run() + + assert Case.dataset.prepare_kwargs["with_scalar_labels"] is True diff --git a/tests/test_cloud_payload_case.py b/tests/test_cloud_payload_case.py new file mode 100644 index 000000000..d3d97fb60 --- /dev/null +++ b/tests/test_cloud_payload_case.py @@ -0,0 +1,181 @@ +import pytest + +from vectordb_bench import config +from vectordb_bench.backend.cases import CaseType, CloudPayloadSearchCase +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.api import EmptyDBCaseConfig +from vectordb_bench.backend.data_source import DatasetSource +from vectordb_bench.backend.dataset import DatasetWithSizeType +from vectordb_bench.backend.payload import PayloadProfile +from vectordb_bench.backend.runner.mp_runner import MultiProcessingSearchRunner +from vectordb_bench.backend.runner.serial_runner import SerialSearchRunner +from vectordb_bench.backend.task_runner import CaseRunner, RunningStatus +from vectordb_bench.cli.cli import get_custom_case_config +from vectordb_bench.models import CaseConfig, TaskConfig + + +class FakeDB: + name = "FakeDB" + + def __init__(self, supported_payload_profiles=None): + self.supported_payload_profiles = supported_payload_profiles or {PayloadProfile.IDS_ONLY} + self.calls = [] + + def supports_payload_profile(self, payload_profile: PayloadProfile) -> bool: + return payload_profile in self.supported_payload_profiles + + def search_embedding(self, query: list[float], k: int = 100, **kwargs) -> list[int]: + self.calls.append((query, k, kwargs)) + return list(range(k)) + + +def test_payload_profile_estimates_response_bytes(): + assert PayloadProfile.IDS_ONLY.estimated_bytes_per_query(k=10, dim=768) == 200 + assert PayloadProfile.VECTOR.estimated_bytes_per_query(k=10, dim=768) == 30_920 + assert PayloadProfile.TEXT.estimated_bytes_per_query(k=10, dim=0) == 5_320 + + +def test_cloud_payload_case_defaults_to_laion_100m(): + case = CloudPayloadSearchCase(payload_profile="vector") + + assert case.case_id == CaseType.CloudPayloadSearchCase + assert case.dataset.data.name == "LAION" + assert case.dataset.data.size == 100_000_000 + assert case.dataset.data.dim == 768 + assert case.payload_profile == PayloadProfile.VECTOR + assert case.estimated_payload_bytes_per_query(config.K_DEFAULT) == 309_200 + + +def test_cloud_payload_case_accepts_sized_dataset(): + case = CloudPayloadSearchCase(dataset_with_size_type=DatasetWithSizeType.CohereSmall.value) + + assert case.dataset_with_size_type == DatasetWithSizeType.CohereSmall + assert case.dataset.data.name == "Cohere" + assert case.dataset.data.size == 100_000 + + +def test_case_config_builds_cloud_payload_case_from_custom_case(): + case = CaseConfig( + case_id=CaseType.CloudPayloadSearchCase, + custom_case={"payload_profile": "vector"}, + ).case + + assert isinstance(case, CloudPayloadSearchCase) + assert case.payload_profile == PayloadProfile.VECTOR + + +def test_case_runner_reuse_key_distinguishes_scalar_label_schema_requirement(): + ids_only_case = CloudPayloadSearchCase( + dataset_with_size_type=DatasetWithSizeType.CohereSmall.value, + payload_profile=PayloadProfile.IDS_ONLY, + ) + scalar_label_case = CloudPayloadSearchCase( + dataset_with_size_type=DatasetWithSizeType.CohereSmall.value, + payload_profile=PayloadProfile.SCALAR_LABEL, + ) + task = TaskConfig( + db=DB.Test, + db_config=DB.Test.config_cls(), + db_case_config=EmptyDBCaseConfig(), + case_config=CaseConfig(case_id=CaseType.CloudPayloadSearchCase), + ) + + ids_only_runner = CaseRunner( + run_id="run-id", + config=task, + ca=ids_only_case, + status=RunningStatus.PENDING, + dataset_source=DatasetSource.S3, + ) + scalar_label_runner = CaseRunner( + run_id="run-id", + config=task, + ca=scalar_label_case, + status=RunningStatus.PENDING, + dataset_source=DatasetSource.S3, + ) + + assert ids_only_case.with_scalar_labels is False + assert scalar_label_case.with_scalar_labels is True + assert ids_only_runner != scalar_label_runner + assert hash(ids_only_runner) != hash(scalar_label_runner) + + +def test_cli_propagates_cloud_payload_dataset_selection(): + custom_case = get_custom_case_config( + { + "case_type": "CloudPayloadSearchCase", + "dataset_with_size_type": DatasetWithSizeType.CohereSmall.value, + "payload_profile": "vector", + "cloud_filter_rate": None, + "cloud_label_percentage": None, + } + ) + + assert custom_case["dataset_with_size_type"] == DatasetWithSizeType.CohereSmall.value + + case = CaseConfig(case_id=CaseType.CloudPayloadSearchCase, custom_case=custom_case).case + assert case.dataset_with_size_type == DatasetWithSizeType.CohereSmall + assert case.dataset.data.size == 100_000 + + +def test_cli_omits_cloud_payload_dataset_when_not_selected(): + custom_case = get_custom_case_config( + { + "case_type": "CloudPayloadSearchCase", + "dataset_with_size_type": None, + "payload_profile": "ids_only", + "cloud_filter_rate": None, + "cloud_label_percentage": None, + } + ) + + assert "dataset_with_size_type" not in custom_case + + case = CaseConfig(case_id=CaseType.CloudPayloadSearchCase, custom_case=custom_case).case + assert case.dataset_with_size_type is None + assert case.dataset.data.name == "LAION" + assert case.dataset.data.size == 100_000_000 + + +def test_serial_runner_omits_payload_argument_for_ids_only(): + db = FakeDB() + runner = SerialSearchRunner(db=db, test_data=[[0.1]], ground_truth=[[0]], k=3) + + assert runner._get_db_search_res([0.1]) == [0, 1, 2] + assert db.calls == [([0.1], 3, {})] + + +def test_serial_runner_passes_payload_argument_for_vector_profile(): + db = FakeDB(supported_payload_profiles={PayloadProfile.IDS_ONLY, PayloadProfile.VECTOR}) + runner = SerialSearchRunner( + db=db, + test_data=[[0.1]], + ground_truth=[[0]], + k=3, + payload_profile=PayloadProfile.VECTOR, + ) + + assert runner._get_db_search_res([0.1]) == [0, 1, 2] + assert db.calls == [([0.1], 3, {"payload_profile": PayloadProfile.VECTOR})] + + +def test_search_runners_fail_fast_for_unsupported_payload_profile(): + db = FakeDB() + + with pytest.raises(NotImplementedError, match="payload_profile=vector"): + SerialSearchRunner( + db=db, + test_data=[[0.1]], + ground_truth=[[0]], + k=3, + payload_profile=PayloadProfile.VECTOR, + ) + + with pytest.raises(NotImplementedError, match="payload_profile=vector"): + MultiProcessingSearchRunner( + db=db, + test_data=[[0.1]], + k=3, + payload_profile=PayloadProfile.VECTOR, + ) diff --git a/tests/test_cloud_payload_search.py b/tests/test_cloud_payload_search.py new file mode 100644 index 000000000..cda8f63ba --- /dev/null +++ b/tests/test_cloud_payload_search.py @@ -0,0 +1,128 @@ +from contextlib import contextmanager + +import polars as pl + +from vectordb_bench.backend.cases import CloudPayloadSearchCase +from vectordb_bench.backend.clients.milvus.milvus import Milvus +from vectordb_bench.backend.data_source import DatasetSource +from vectordb_bench.backend.dataset import Dataset +from vectordb_bench.backend.filter import FilterOp, non_filter +from vectordb_bench.backend.payload import PayloadProfile +from vectordb_bench.backend.runner.serial_runner import SerialSearchRunner + + +class _Client: + def __init__(self): + self.search_kwargs = None + + def search(self, **kwargs): + self.search_kwargs = kwargs + return [[{"pk": 1}]] + + +def test_laion_100m_declares_scalar_label_assets(): + laion = Dataset.LAION.get(100_000_000) + + assert laion.with_scalar_labels is True + assert laion.scalar_labels_file == "scalar_labels.parquet" + assert 0.01 in laion.scalar_label_percentages + + +def test_scalar_label_payload_profile_estimates_small_string_payload(): + assert PayloadProfile.SCALAR_LABEL.estimated_bytes_per_query(k=100, dim=768) == 3600 + + +def test_scalar_label_payload_profile_requires_scalar_label_materialization_without_filter(): + case = CloudPayloadSearchCase(payload_profile="scalar_label") + + assert case.filters.type == FilterOp.NonFilter + assert case.with_scalar_labels is True + + +def test_dataset_prepare_loads_separated_scalar_labels_for_scalar_payload(monkeypatch): + dataset = Dataset.LAION.manager(100_000_000) + dataset.data.with_remote_resource = False + loaded_scalar_labels = object() + + def fake_read_file(file_name): + if file_name == dataset.data.scalar_labels_file: + return loaded_scalar_labels + return pl.DataFrame({dataset.data.test_vector_field: [], dataset.data.gt_neighbors_field: []}) + + monkeypatch.setattr(dataset, "_read_file", fake_read_file) + + dataset.prepare( + source=DatasetSource.S3, + filters=non_filter, + with_train_files=False, + with_scalar_labels=True, + ) + + assert dataset.scalar_labels is loaded_scalar_labels + + +def test_cloud_payload_case_can_combine_label_filter_with_scalar_label_payload(): + case = CloudPayloadSearchCase( + payload_profile="scalar_label", + label_percentage=0.01, + ) + + assert case.payload_profile == PayloadProfile.SCALAR_LABEL + assert case.filters.type == FilterOp.StrEqual + assert case.filters.label_value == "label_1p" + + +def test_milvus_scalar_label_payload_requests_label_output_field(): + db = Milvus.__new__(Milvus) + db.client = _Client() + db.case_config = type("CaseConfig", (), {"search_param": lambda self: {}})() + db.collection_name = "collection" + db.expr = "label == 'label_1p'" + db._primary_field = "pk" + db._vector_field = "vector" + db._scalar_label_field = "label" + + assert db.supports_payload_profile(PayloadProfile.SCALAR_LABEL) + assert db.search_embedding([0.1, 0.2], payload_profile=PayloadProfile.SCALAR_LABEL) == [1] + assert db.client.search_kwargs["output_fields"] == ["label"] + + +class TenantSearchProbeDB: + name = "TenantSearchProbeDB" + + def __init__(self): + self.tenants = [] + + def supports_payload_profile(self, payload_profile): + return True + + @contextmanager + def init(self): + yield + + def prepare_filter(self, filters): + return None + + def search_embedding(self, query, k=100, payload_profile=None, tenant=None): + self.tenants.append(tenant) + return [] + + +def test_serial_search_runner_passes_tenant_and_skips_recall(): + db = TenantSearchProbeDB() + runner = SerialSearchRunner( + db=db, + test_data=[[1.0, 0.0], [0.0, 1.0]], + ground_truth=None, + tenant_labels=["tenant_0000", "tenant_0001"], + measure_recall=False, + ) + + recall, ndcg, p99, p95 = runner.search((runner.test_data, runner.ground_truth)) + + assert recall == 0 + assert ndcg == 0 + assert p99 >= 0 + assert p95 >= 0 + assert set(db.tenants).issubset({"tenant_0000", "tenant_0001"}) + assert db.tenants diff --git a/tests/test_concurrent_runner.py b/tests/test_concurrent_runner.py new file mode 100644 index 000000000..c9e5d9267 --- /dev/null +++ b/tests/test_concurrent_runner.py @@ -0,0 +1,159 @@ +"""Tests for ConcurrentInsertRunner against a running Milvus instance. + +Includes: + - Correctness tests (threading & async backends) + - Parameterized benchmark: serial vs concurrent across (batch_size, workers) matrix + +NUM_PER_BATCH is set via os.environ before each run. Since runners execute +task() in a spawn subprocess that re-imports config, the env var takes effect. + +Requires: + - Milvus running at localhost:19530 + - Network access to download OpenAI 50K dataset + +Usage: + pytest tests/test_concurrent_runner.py -v -s # correctness tests only + python tests/test_concurrent_runner.py # full benchmark matrix +""" + +# ruff: noqa: T201 + +from __future__ import annotations + +import logging +import os +import time + +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.milvus.config import FLATConfig +from vectordb_bench.backend.dataset import Dataset, DatasetSource +from vectordb_bench.backend.runner.concurrent_runner import ConcurrentInsertRunner, ExecutorBackend +from vectordb_bench.backend.runner.serial_runner import SerialInsertRunner + +log = logging.getLogger("vectordb_bench") +log.setLevel(logging.INFO) + +DATASET_SIZE = 50_000 + + +# ── Shared helpers ────────────────────────────────────────────────────── + + +def get_milvus_db(collection_name: str): + return DB.Milvus.init_cls( + dim=1536, + db_config={"uri": "http://localhost:19530", "user": "", "password": ""}, + db_case_config=FLATConfig(metric_type="COSINE"), + collection_name=collection_name, + drop_old=True, + ) + + +def prepare_dataset(): + dataset = Dataset.OPENAI.manager(DATASET_SIZE) + dataset.prepare(DatasetSource.AliyunOSS) + return dataset + + +def set_batch_size(batch_size: int) -> None: + os.environ["NUM_PER_BATCH"] = str(batch_size) + + +def timed_run(runner: SerialInsertRunner | ConcurrentInsertRunner) -> tuple[int, float]: + start = time.perf_counter() + count = runner.run() + return count, time.perf_counter() - start + + +# ── Correctness tests (pytest) ────────────────────────────────────────── + + +def test_concurrent_insert_threading(): + """Test concurrent insert with threading backend.""" + db = get_milvus_db("test_conc_threading") + runner = ConcurrentInsertRunner( + db=db, + dataset=prepare_dataset(), + normalize=False, + max_workers=4, + backend=ExecutorBackend.THREADING, + ) + count = runner.run() + assert count == DATASET_SIZE, f"Expected {DATASET_SIZE}, got {count}" + + +def test_concurrent_insert_async(): + """Test concurrent insert with async backend.""" + db = get_milvus_db("test_conc_async") + runner = ConcurrentInsertRunner( + db=db, + dataset=prepare_dataset(), + normalize=False, + max_workers=4, + backend=ExecutorBackend.ASYNC, + ) + count = runner.run() + assert count == DATASET_SIZE, f"Expected {DATASET_SIZE}, got {count}" + + +# ── Parameterized benchmark ──────────────────────────────────────────── + + +def run_serial(batch_size: int) -> tuple[int, float]: + set_batch_size(batch_size) + runner = SerialInsertRunner( + db=get_milvus_db(f"bench_serial_b{batch_size}"), + dataset=prepare_dataset(), + normalize=False, + ) + return timed_run(runner) + + +def run_concurrent(batch_size: int, workers: int) -> tuple[int, float]: + set_batch_size(batch_size) + runner = ConcurrentInsertRunner( + db=get_milvus_db(f"bench_conc_b{batch_size}_w{workers}"), + dataset=prepare_dataset(), + normalize=False, + max_workers=workers, + backend=ExecutorBackend.THREADING, + ) + return timed_run(runner) + + +def bench_matrix(): + batch_sizes = [100, 500, 1000, 5000] + worker_counts = [1, 2, 4, 8] + + conc_headers = [f"conc({w}w)" for w in worker_counts] + speedup_headers = [f"speedup({w}w)" for w in worker_counts] + print(f"\n{'Batch':>6} {'#Bat':>5} {'serial':>8}", end="") + for h in conc_headers: + print(f" {h:>10}", end="") + for h in speedup_headers: + print(f" {h:>12}", end="") + print() + print("-" * (22 + 10 * len(worker_counts) + 12 * len(worker_counts))) + + for bs in batch_sizes: + n_batches = DATASET_SIZE // bs + _, dur_s = run_serial(bs) + + conc_durs = [] + for w in worker_counts: + _, dur_c = run_concurrent(bs, w) + conc_durs.append(dur_c) + + print(f"{bs:>6} {n_batches:>5} {dur_s:>7.2f}s", end="") + for dur_c in conc_durs: + print(f" {dur_c:>9.2f}s", end="") + for dur_c in conc_durs: + print(f" {dur_s / dur_c:>11.2f}x", end="") + print() + + # restore default + set_batch_size(100) + + +if __name__ == "__main__": + bench_matrix() diff --git a/tests/test_db_client_resolution.py b/tests/test_db_client_resolution.py new file mode 100644 index 000000000..ce0278eb0 --- /dev/null +++ b/tests/test_db_client_resolution.py @@ -0,0 +1,17 @@ +"""Every DB must resolve config_cls/init_cls or raise ModuleNotFoundError. +Anything else breaks the Run Test page's missing-optional-dep hint path. +""" + +import pytest + +from vectordb_bench.backend.clients import DB + + +@pytest.mark.parametrize("db", list(DB), ids=lambda d: d.name) +def test_db_resolves_or_missing_module(db): + for attr in ("config_cls", "init_cls"): + try: + getattr(db, attr) + except ModuleNotFoundError as e: + assert e.name, f"{db.name}.{attr}: ModuleNotFoundError has no .name" + return diff --git a/tests/test_filtered_adapters.py b/tests/test_filtered_adapters.py new file mode 100644 index 000000000..a5d0d713f --- /dev/null +++ b/tests/test_filtered_adapters.py @@ -0,0 +1,79 @@ +from importlib import import_module +from typing import Any + +import pytest + +from vectordb_bench.backend.filter import FilterOp, IntFilter, non_filter + + +@pytest.fixture +def numeric_filter() -> IntFilter: + return IntFilter(filter_rate=0.99, int_field="id", int_value=49_500) + + +def _adapter_class(dependency: str, module: str, name: str) -> Any: + pytest.importorskip(dependency) + return getattr(import_module(module), name) + + +def test_qdrant_numeric_filter_is_inclusive(numeric_filter: IntFilter): + adapter_cls = _adapter_class( + "qdrant_client", + "vectordb_bench.backend.clients.qdrant_local.qdrant_local", + "QdrantLocal", + ) + + assert FilterOp.NumGE in adapter_cls.supported_filter_types + adapter = adapter_cls.__new__(adapter_cls) + adapter._primary_field = "pk" + adapter.prepare_filter(numeric_filter) + numeric_range = adapter._query_filter.must[0].range + assert numeric_range.gte == 49_500 + assert numeric_range.gt is None + adapter.prepare_filter(non_filter) + assert adapter._query_filter is None + + +def test_weaviate_numeric_filter_is_inclusive(numeric_filter: IntFilter): + adapter_cls = _adapter_class( + "weaviate", + "vectordb_bench.backend.clients.weaviate_cloud.weaviate_cloud", + "WeaviateCloud", + ) + + assert FilterOp.NumGE in adapter_cls.supported_filter_types + adapter = adapter_cls.__new__(adapter_cls) + adapter._scalar_field = "key" + adapter.prepare_filter(numeric_filter) + assert adapter._where_filter == { + "path": ["key"], + "operator": "GreaterThanEqual", + "valueInt": 49_500, + } + + +def test_chroma_numeric_filter_is_inclusive(numeric_filter: IntFilter): + adapter_cls = _adapter_class( + "chromadb", + "vectordb_bench.backend.clients.chroma.chroma", + "ChromaClient", + ) + + assert FilterOp.NumGE in adapter_cls.supported_filter_types + adapter = adapter_cls.__new__(adapter_cls) + adapter.prepare_filter(numeric_filter) + assert adapter._where_filter == {"index": {"$gte": 49_500}} + + +def test_elasticsearch_numeric_filter_is_inclusive(numeric_filter: IntFilter): + adapter_cls = _adapter_class( + "elasticsearch", + "vectordb_bench.backend.clients.elastic_cloud.elastic_cloud", + "ElasticCloud", + ) + + assert FilterOp.NumGE in adapter_cls.supported_filter_types + adapter = adapter_cls.__new__(adapter_cls) + adapter.id_col_name = "id" + adapter.prepare_filter(numeric_filter) + assert adapter.filter == {"range": {"id": {"gte": 49_500}}} diff --git a/tests/test_fts_backend_filters.py b/tests/test_fts_backend_filters.py new file mode 100644 index 000000000..830fa9c60 --- /dev/null +++ b/tests/test_fts_backend_filters.py @@ -0,0 +1,177 @@ +# ruff: noqa: ANN001, ARG001, E402 + +import io +import sys +import threading +import types + +import pytest + +sys.modules.setdefault("turbopuffer", types.SimpleNamespace(Turbopuffer=object)) + +from vectordb_bench.backend.clients.elastic_cloud.config import ElasticCloudFtsConfig +from vectordb_bench.backend.clients.elastic_cloud.elastic_cloud import ElasticCloud +from vectordb_bench.backend.clients.milvus.config import MilvusFtsConfig +from vectordb_bench.backend.clients.milvus.milvus import Milvus +from vectordb_bench.backend.clients.turbopuffer.turbopuffer import TurboPuffer +from vectordb_bench.backend.clients.vespa.config import VespaFtsConfig +from vectordb_bench.backend.clients.vespa.vespa import Vespa +from vectordb_bench.backend.filter import NewIntFilter + + +def test_milvus_fts_insert_and_search_use_filter_id(): + db = Milvus.__new__(Milvus) + db._is_fts = True + db.name = "Milvus" + db.collection_name = "col" + db.batch_size = 1000 + db.with_scalar_labels = False + db._primary_field = "doc_id" + db._text_field = "text" + db._filter_id_field = "filter_id" + db._sparse_field = "sparse" + db.case_config = MilvusFtsConfig() + calls = {} + + class Client: + def insert(self, collection_name, rows): + calls["insert"] = (collection_name, rows) + return {"insert_count": len(rows)} + + def search(self, **kwargs): + calls["search"] = kwargs + return [[{"entity": {"doc_id": "d1"}}]] + + db.client = Client() + + assert db.insert_documents(["alpha"], ["d1"], filter_ids=[12]) == (1, None) + db.prepare_filter(NewIntFilter(filter_rate=0.5, int_field="filter_id", int_value=10)) + assert db.search_documents("alpha") == ["d1"] + + assert calls["insert"] == ("col", [{"doc_id": "d1", "text": "alpha", "filter_id": 12}]) + assert calls["search"]["filter"] == "filter_id >= 10" + + +def test_elastic_cloud_fts_insert_and_search_use_filter_id(monkeypatch): + db = ElasticCloud.__new__(ElasticCloud) + db._is_fts = True + db.indice = "idx" + db.id_col_name = "doc_id" + db.text_col_name = "text" + db.filter_id_col_name = "filter_id" + db.filter = None + db.client = object() + bulk_calls = [] + + def fake_bulk(client, actions): + bulk_calls.append(actions) + return len(actions), [] + + monkeypatch.setattr("vectordb_bench.backend.clients.elastic_cloud.elastic_cloud.bulk", fake_bulk) + assert db.insert_documents(["alpha"], ["d1"], filter_ids=[3]) == (1, None) + assert bulk_calls == [ + [ + { + "_index": "idx", + "_id": "d1", + "_source": {"doc_id": "d1", "text": "alpha", "filter_id": 3}, + } + ] + ] + + search_calls = {} + + class Client: + def search(self, **kwargs): + search_calls.update(kwargs) + return {"hits": {"hits": [{"fields": {"doc_id": ["d1"]}}]}} + + db.client = Client() + db.prepare_filter(NewIntFilter(filter_rate=0.5, int_field="filter_id", int_value=3)) + assert db.search_documents("alpha") == ["d1"] + assert search_calls["query"] == { + "bool": { + "must": {"match": {"text": "alpha"}}, + "filter": {"range": {"filter_id": {"gte": 3}}}, + } + } + + +def test_elastic_cloud_fts_mapping_has_filter_id(): + assert ElasticCloudFtsConfig().index_param()["properties"]["filter_id"] == {"type": "long"} + + +def test_vespa_fts_feed_and_search_use_filter_id(): + db = Vespa.__new__(Vespa) + db._is_fts = True + db.schema_name = "schema" + db._text_field = "text" + db._filter_id_field = "filter_id" + db._filter_expr = None + db.case_config = VespaFtsConfig() + db._feed_lock = threading.Lock() + db._feed_written_count = 0 + output = io.StringIO() + + db._ensure_fts_feed_client = lambda: types.SimpleNamespace( + poll=lambda: None, + stdin=types.SimpleNamespace(write=output.write, flush=lambda: None), + ) + + db._write_fts_feed_batch(["alpha"], ["d1"], [9]) + assert '"filter_id":9' in output.getvalue() + + calls = {} + + class Result: + def get_json(self): + return {"root": {"children": [{"fields": {"id": "d1"}}]}} + + def query(payload): + calls["payload"] = payload + return Result() + + db.client = types.SimpleNamespace(query=query) + db.prepare_filter(NewIntFilter(filter_rate=0.5, int_field="filter_id", int_value=9)) + assert db.search_documents("alpha") == ["d1"] + assert calls["payload"]["yql"] == "select id from schema where userQuery() and filter_id >= 9" + + +def test_turbopuffer_fts_insert_and_search_use_filter_id(): + db = TurboPuffer.__new__(TurboPuffer) + db._is_fts = True + db._text_field = "text" + db._scalar_id_field = "id" + db._filter_id_field = "filter_id" + db.expr = None + db.db_case_config = types.SimpleNamespace(disable_backpressure=False) + write_calls = {} + query_calls = {} + + class Namespace: + def write(self, **kwargs): + write_calls.update(kwargs) + + def query(self, **kwargs): + query_calls.update(kwargs) + return types.SimpleNamespace(rows=[types.SimpleNamespace(id="d1")]) + + db.ns = Namespace() + + assert db.insert_documents(["alpha"], ["d1"], filter_ids=[5]) == (1, None) + db.prepare_filter(NewIntFilter(filter_rate=0.5, int_field="filter_id", int_value=5)) + assert db.search_documents("alpha") == ["d1"] + + assert write_calls["upsert_columns"]["filter_id"] == [5] + assert query_calls["filters"] == ("filter_id", "Gte", 5) + + +@pytest.mark.parametrize("db_cls", [Milvus, ElasticCloud, Vespa, TurboPuffer]) +def test_fts_filter_rejects_non_filter_id_field(db_cls): + db = db_cls.__new__(db_cls) + db._is_fts = True + db._filter_id_field = "filter_id" + db.filter_id_col_name = "filter_id" + + with pytest.raises(ValueError, match="filter_id"): + db.prepare_filter(NewIntFilter(filter_rate=0.5, int_field="id", int_value=1)) diff --git a/tests/test_fts_cases.py b/tests/test_fts_cases.py new file mode 100644 index 000000000..848bc3b45 --- /dev/null +++ b/tests/test_fts_cases.py @@ -0,0 +1,47 @@ +import pytest + +from vectordb_bench.backend.cases import FTS_FILTER_ID_FIELD, FTSBm25Performance +from vectordb_bench.backend.dataset import FtsDatasetWithSizeType +from vectordb_bench.backend.filter import FilterOp + + +def test_fts_filter_case_uses_filter_id_for_large_dataset(): + case = FTSBm25Performance( + dataset_with_size_type=FtsDatasetWithSizeType.MSMarcoLarge, + filter_rate=0.95, + ) + + filters = case.filters + + assert filters.type == FilterOp.NumGE + assert filters.int_field == FTS_FILTER_ID_FIELD + assert filters.int_value == int(8_841_823 * 0.95) + assert "Filter 95%" in case.name + + +def test_fts_filter_case_rejects_small_and_medium_datasets(): + with pytest.raises(ValueError, match="only supported"): + FTSBm25Performance( + dataset_with_size_type=FtsDatasetWithSizeType.MSMarcoSmall, + filter_rate=0.5, + ) + + with pytest.raises(ValueError, match="only supported"): + FTSBm25Performance( + dataset_with_size_type=FtsDatasetWithSizeType.HotpotQAMedium, + filter_rate=0.5, + ) + + +def test_fts_filter_case_rejects_unsupported_filter_rate(): + with pytest.raises(ValueError, match="must be one of"): + FTSBm25Performance( + dataset_with_size_type=FtsDatasetWithSizeType.HotpotQALarge, + filter_rate=0.8, + ) + + +def test_fts_filter_case_has_no_distribution_switch(): + case = FTSBm25Performance(dataset_with_size_type=FtsDatasetWithSizeType.MSMarcoLarge) + + assert not hasattr(case, "filter_id_distribution") diff --git a/tests/test_fts_cli_user_control.py b/tests/test_fts_cli_user_control.py new file mode 100644 index 000000000..1496b3bc8 --- /dev/null +++ b/tests/test_fts_cli_user_control.py @@ -0,0 +1,94 @@ +from typing import get_type_hints + +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.api import IndexType +from vectordb_bench.backend.clients.elastic_cloud.config import ElasticCloudFtsConfig, ElasticCloudIndexConfig +from vectordb_bench.backend.clients.milvus.config import MilvusFtsConfig +from vectordb_bench.backend.clients.vespa.config import VespaFtsConfig, VespaHNSWConfig +from vectordb_bench.backend.dataset import FtsDatasetWithSizeType +from vectordb_bench.cli.cli import CommonTypedDict, get_custom_case_config, select_cli_db_case_config + + +def test_common_cli_exposes_optional_fts_bm25_overrides(): + hints = get_type_hints(CommonTypedDict, include_extras=True) + + assert "bm25_k1" in hints + assert "bm25_b" in hints + assert "fts_filter_rate" in hints + assert "fts_filter_id_distribution" not in hints + + +def test_cli_builds_fts_filter_case_config(): + custom_case = get_custom_case_config( + { + "case_type": "FTSBm25Performance", + "dataset_with_size_type": FtsDatasetWithSizeType.MSMarcoLarge.value, + "payload_profile": "ids_only", + "fts_filter_rate": 0.95, + } + ) + + assert custom_case == { + "dataset_with_size_type": FtsDatasetWithSizeType.MSMarcoLarge.value, + "payload_profile": "ids_only", + "filter_rate": 0.95, + } + + +def test_cli_applies_bm25_overrides_to_existing_fts_config(): + config = MilvusFtsConfig(drop_ratio_search=0.1) + + selected = select_cli_db_case_config( + DB.Milvus, + config, + "FTSBm25Performance", + {"bm25_k1": 1.4, "bm25_b": 0.6}, + ) + + assert isinstance(selected, MilvusFtsConfig) + assert selected.bm25_k1 == 1.4 + assert selected.bm25_b == 0.6 + assert selected.drop_ratio_search == 0.1 + assert selected.sparse_index_param()["params"] == { + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": 1.4, + "bm25_b": 0.6, + } + + +def test_cli_applies_bm25_overrides_after_routing_vector_config_to_fts(): + selected = select_cli_db_case_config( + DB.ElasticCloud, + ElasticCloudIndexConfig(index=IndexType.ES_HNSW, number_of_shards=3), + "FTSBm25Performance", + {"bm25_k1": 1.7, "bm25_b": 0.2}, + ) + + assert isinstance(selected, ElasticCloudFtsConfig) + assert selected.number_of_shards == 3 + assert selected.bm25_k1 == 1.7 + assert selected.bm25_b == 0.2 + assert selected.index_param()["properties"]["text"]["similarity"] == "vdbbench_bm25" + assert selected.similarity_settings() == { + "similarity": { + "vdbbench_bm25": { + "type": "BM25", + "k1": 1.7, + "b": 0.2, + } + } + } + + +def test_cli_leaves_fts_bm25_defaults_when_options_are_omitted(): + selected = select_cli_db_case_config( + DB.Vespa, + VespaHNSWConfig(), + "FTSBm25Performance", + {"bm25_k1": None, "bm25_b": None}, + ) + + assert isinstance(selected, VespaFtsConfig) + assert selected.bm25_k1 is None + assert selected.bm25_b is None + assert selected.rank_properties() == [] diff --git a/tests/test_fts_dataset.py b/tests/test_fts_dataset.py new file mode 100644 index 000000000..a5eaf9791 --- /dev/null +++ b/tests/test_fts_dataset.py @@ -0,0 +1,301 @@ +import math +from dataclasses import dataclass +from typing import ClassVar + +import pytest + +from vectordb_bench.backend.dataset import ( + FtsDataset, + FtsDatasetManager, + FtsDatasetWithSizeType, + FtsFilterIdPermutation, + HotpotQAFts, + HotpotQATranslator, + MSMarcoFts, + MSMarcoTranslator, + SizeLabel, +) +from vectordb_bench.backend.filter import NewIntFilter + + +@dataclass +class Query: + query_id: str + text: str + + +@dataclass +class Doc: + doc_id: str + text: str + title: str = "" + + +@dataclass +class Qrel: + query_id: str + doc_id: str + relevance: int + + +class FakeDataset: + def __init__(self): + self.queries = [Query("q1", "alpha query"), Query("q2", "beta query")] + self.docs = [ + Doc("d1", "alpha document", "A"), + Doc("d2", "beta document", "B"), + Doc("d3", "gamma document", "C"), + Doc("d4", "delta document", "D"), + ] + self.qrels = [Qrel("q1", "d3", 1), Qrel("q1", "d2", 0), Qrel("q2", "d1", 2)] + + def queries_iter(self): + yield from self.queries + + def docs_iter(self): + yield from self.docs + + def qrels_iter(self): + yield from self.qrels + + +class FakeDatasetWithMissingQrel(FakeDataset): + def __init__(self): + super().__init__() + self.qrels = [Qrel("q1", "missing", 1)] + + +class FakeDatasetWithLowPermutationQrels(FakeDataset): + def __init__(self): + super().__init__() + self.qrels = [Qrel("q1", "d3", 1), Qrel("q2", "d4", 2)] + + +def make_tiny_msmarco_manager(size: int = 3) -> FtsDatasetManager: + small_label = MSMarcoFts._size_label[100_000] + + class TinyMSMarcoFts(MSMarcoFts): + _size_label: ClassVar[dict[int, SizeLabel]] = { + **MSMarcoFts._size_label, + size: small_label._replace(size=size), + } + + return FtsDatasetManager(data=TinyMSMarcoFts(size=size)) + + +def test_msmarco_translator_uses_string_ids_and_qrels(): + translator = MSMarcoTranslator() + dataset = FakeDataset() + + query = translator.translate_query(Query("10", "hello")) + document = translator.translate_document(Doc("20", "hello\tworld\nagain")) + ground_truth = translator.load_ground_truth(dataset) + + assert query.query_id == "10" + assert document.doc_id == "20" + assert document.text == "hello world again" + assert ground_truth == {"q1": {"d3": 1}, "q2": {"d1": 2}} + + +def test_hotpotqa_translator_combines_title_and_text_and_uses_qrels(): + translator = HotpotQATranslator() + dataset = FakeDataset() + + document = translator.translate_document(Doc("doc-a", "body", "Title")) + ground_truth = translator.load_ground_truth(dataset) + + assert document.doc_id == "doc-a" + assert document.text == "Title body" + assert ground_truth == {"q1": {"d3": 1}, "q2": {"d1": 2}} + + +def test_fts_filter_id_permutation_has_stable_sequence(): + permutation = FtsFilterIdPermutation.for_size(8) + + assert permutation.algorithm == "affine_permutation_v1" + assert permutation.multiplier == 5 + assert permutation.offset == 3 + assert [permutation.map(i) for i in range(8)] == [3, 0, 5, 2, 7, 4, 1, 6] + assert permutation == FtsFilterIdPermutation.for_size(8) + + +@pytest.mark.parametrize("size", [1, 2, 3, 4, 8, 10, 97, 100, 1_000, 100_000, 1_000_000, 5_233_329, 8_841_823]) +def test_fts_filter_id_permutation_is_bijective(size: int): + permutation = FtsFilterIdPermutation.for_size(size) + + assert math.gcd(permutation.multiplier, size) == 1 + assert 0 <= permutation.offset < size + if size <= 1_000: + assert sorted(permutation.map(i) for i in range(size)) == list(range(size)) + + +@pytest.mark.parametrize(("size", "filter_rate"), [(8, 0.5), (100, 0.99), (101, 0.75)]) +def test_fts_filter_id_permutation_preserves_exact_selectivity(size: int, filter_rate: float): + permutation = FtsFilterIdPermutation.for_size(size) + filter_value = int(size * filter_rate) + + matched = sum(permutation.map(i) >= filter_value for i in range(size)) + + assert matched == size - filter_value + + +def test_fts_filter_id_permutation_scatters_matches_across_corpus_order(): + permutation = FtsFilterIdPermutation.for_size(100) + + matching_ordinals = [ordinal for ordinal in range(100) if permutation.map(ordinal) >= 90] + + assert matching_ordinals != list(range(90, 100)) + assert len({ordinal // 10 for ordinal in matching_ordinals}) >= 6 + + +def test_fts_filter_id_permutation_validates_bounds(): + with pytest.raises(ValueError, match="size must be positive"): + FtsFilterIdPermutation.for_size(0) + + permutation = FtsFilterIdPermutation.for_size(3) + with pytest.raises(ValueError, match="ordinal must be in"): + permutation.map(3) + + +def test_fts_iterator_preserves_qrel_docs_before_filler(): + manager = make_tiny_msmarco_manager() + manager._ir_dataset = FakeDataset() + manager.required_doc_ids = {"d4"} + manager.selected_doc_ids = manager._build_selected_doc_ids() + + assert manager.selected_doc_ids == {"d1", "d2", "d4"} + + batches = list(manager) + docs = [(doc.doc_id, doc.filter_id) for batch in batches for doc in batch] + + assert len(docs) == 3 + assert ("d4", 1) in docs + assert all(not doc_id.isdecimal() for doc_id, _ in docs) + assert [filter_id for _, filter_id in docs] == [2, 0, 1] + + +def test_fts_qrel_filter_ids_match_sparse_emitted_documents_when_one_is_skipped(): + class UnassignableDocument: + doc_id = "d3" + text = "malformed" + + @property + def filter_id(self) -> int | None: + return None + + @filter_id.setter + def filter_id(self, value: int | None) -> None: # noqa: ARG002 + raise ValueError("cannot assign filter_id") + + class SparseTranslator: + def iter_documents(self, dataset): # noqa: ARG002 + yield Doc("d1", "one") + yield Doc("d2", "two") + yield UnassignableDocument() + yield Doc("d4", "four") + yield Doc("d5", "five") + + manager = make_tiny_msmarco_manager(size=3) + manager._ir_dataset = object() + manager._translator = SparseTranslator() + manager.required_doc_ids = {"d1", "d5"} + manager.selected_doc_ids = {"d1", "d3", "d5"} + + qrel_filter_ids = manager._build_qrel_filter_ids() + emitted_filter_ids = {doc.doc_id: doc.filter_id for batch in manager for doc in batch} + + assert emitted_filter_ids == {"d1": 2, "d5": 0} + assert qrel_filter_ids == emitted_filter_ids + + +def test_fts_prepare_integer_filter_derives_filtered_qrels(monkeypatch: pytest.MonkeyPatch): + manager = make_tiny_msmarco_manager(size=4) + monkeypatch.setattr(manager._translator, "load", FakeDataset) + + filters = NewIntFilter(filter_rate=0.5, int_field="filter_id", int_value=2) + assert manager.prepare(source=None, filters=filters) + + assert [query.query_id for query in manager.queries_data] == ["q1", "q2"] + assert manager.gt_data == [{"d3": 1}, {"d1": 2}] + assert [query.query_id for query in manager.recall_queries_data] == ["q2"] + assert manager.recall_gt_data == [{"d1": 2}] + assert manager.recall_skipped is False + assert manager.recall_skip_reason is None + assert manager.qrel_filter_ids == {"d1": 3, "d3": 1} + assert manager.filter_stats == { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 2, + "filter_rate": 0.5, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3, + "filter_id_offset": 3, + "matched_doc_count": 2, + "matched_doc_ratio": 0.5, + "original_query_count": 2, + "filtered_query_count": 1, + "filtered_query_ratio": 0.5, + "original_relevant_doc_count": 2, + "filtered_relevant_doc_count": 1, + } + + +def test_fts_prepare_integer_filter_skips_empty_filtered_qrels(monkeypatch: pytest.MonkeyPatch): + manager = make_tiny_msmarco_manager(size=4) + monkeypatch.setattr(manager._translator, "load", FakeDatasetWithLowPermutationQrels) + + filters = NewIntFilter(filter_rate=0.75, int_field="filter_id", int_value=3) + assert manager.prepare(source=None, filters=filters) + + assert [query.query_id for query in manager.queries_data] == ["q1", "q2"] + assert manager.gt_data == [{"d3": 1}, {"d4": 2}] + assert manager.recall_queries_data == [] + assert manager.recall_gt_data == [] + assert manager.recall_skipped is True + assert manager.recall_skip_reason == "no_positive_qrels_after_filter" + assert manager.filter_stats["filtered_query_count"] == 0 + assert manager.filter_stats["filtered_relevant_doc_count"] == 0 + + +def test_fts_prepare_integer_filter_requires_filter_id_field(monkeypatch: pytest.MonkeyPatch): + manager = make_tiny_msmarco_manager(size=4) + monkeypatch.setattr(manager._translator, "load", FakeDataset) + + filters = NewIntFilter(filter_rate=0.5, int_field="id", int_value=2) + with pytest.raises(ValueError, match="int_field='filter_id'"): + manager.prepare(source=None, filters=filters) + + +def test_fts_cap_rejects_required_qrel_docs_missing_from_corpus(): + manager = make_tiny_msmarco_manager() + manager._ir_dataset = FakeDataset() + manager.required_doc_ids = {"missing"} + + with pytest.raises(ValueError, match="missing from corpus"): + manager._build_selected_doc_ids() + + +def test_fts_prepare_propagates_missing_required_qrel_docs(monkeypatch: pytest.MonkeyPatch): + manager = FtsDatasetManager(data=MSMarcoFts(size=100_000)) + monkeypatch.setattr(manager._translator, "load", FakeDatasetWithMissingQrel) + + with pytest.raises(ValueError, match="missing from corpus"): + manager.prepare(source=None) + + +def test_fts_dataset_size_registry(): + assert FtsDataset.MSMARCO.manager(100_000).data.full_name == "MS MARCO FTS (SMALL)" + assert FtsDataset.MSMARCO.manager(1_000_000).data.full_name == "MS MARCO FTS (MEDIUM)" + assert FtsDataset.HOTPOTQA.manager(5_233_329).data.full_name == "HotpotQA FTS (LARGE)" + assert FtsDatasetWithSizeType.MSMarcoSmall.get_manager().data.size == 100_000 + assert FtsDatasetWithSizeType.HotpotQAMedium.get_manager().data.size == 1_000_000 + with pytest.raises(ValueError, match="not supported"): + FtsDataset.MSMARCO.manager(3) + + +def test_cap_smaller_than_required_qrels_is_invalid(): + manager = FtsDatasetManager(data=HotpotQAFts(size=100_000)) + manager.qrels_data = {"q1": {"a": 1, "b": 1, "c": 1}} + + with pytest.raises(ValueError, match="requires 3 qrel documents"): + manager._validate_cap(required_doc_ids={"a", "b", "c"}, target_size=2) diff --git a/tests/test_fts_filter_runner.py b/tests/test_fts_filter_runner.py new file mode 100644 index 000000000..fe7179334 --- /dev/null +++ b/tests/test_fts_filter_runner.py @@ -0,0 +1,182 @@ +import threading + +from vectordb_bench.backend.cases import CaseLabel +from vectordb_bench.backend.data_source import DatasetSource +from vectordb_bench.backend.dataset import FtsDocument, FtsQuery +from vectordb_bench.backend.filter import non_filter +from vectordb_bench.backend.payload import PayloadProfile +from vectordb_bench.backend.runner.concurrent_runner import ConcurrentInsertRunner +from vectordb_bench.backend.task_runner import CaseRunner +from vectordb_bench.models import TaskStage + + +def test_concurrent_insert_runner_fts_batch_includes_filter_ids(): + runner = ConcurrentInsertRunner.__new__(ConcurrentInsertRunner) + runner._deadline = None + runner._stop_event = None + runner._iter_lock = threading.Lock() + runner._prefetched_fts_batch = None + runner._dataset_iter = iter( + [ + [ + FtsDocument(doc_id="d1", text="alpha", filter_id=0), + FtsDocument(doc_id="d2", text="beta", filter_id=5), + ] + ] + ) + + assert runner._next_fts_batch() == { + "texts": ["alpha", "beta"], + "doc_ids": ["d1", "d2"], + "filter_ids": [0, 5], + } + + +def test_fts_pre_run_passes_filters_to_dataset(monkeypatch): + filter_obj = object() + + class Dataset: + def __init__(self): + self.calls = [] + + def prepare(self, source, filters=None): + self.calls.append((source, filters)) + + class Case: + label = CaseLabel.FullTextSearchPerformance + is_multitenant = False + dataset = Dataset() + filters = filter_obj + + config_obj = type("Config", (), {"stages": [TaskStage.LOAD]})() + runner = CaseRunner.construct(ca=Case(), config=config_obj, dataset_source=DatasetSource.S3) + init_calls = [] + monkeypatch.setattr(CaseRunner, "init_db", lambda self, drop_old=True: init_calls.append(drop_old)) + + runner._pre_run(drop_old=False) + + assert runner.ca.dataset.calls == [(DatasetSource.S3, filter_obj)] + assert init_calls == [False] + + +def test_fts_perf_metric_includes_dataset_filter_stats(): + class Dataset: + filter_stats = { + "filter_field": "filter_id", + "filter_value": 90, + "filtered_query_count": 12, + } + + class Case: + label = CaseLabel.FullTextSearchPerformance + dataset = Dataset() + + config_obj = type("Config", (), {"stages": []})() + runner = CaseRunner.construct(ca=Case(), config=config_obj) + + metric = runner._run_perf_case(drop_old=False) + + assert metric.additional_parameters["fts_filter"] == Dataset.filter_stats + + +class DummyDb: + name = "dummy" + + def supports_payload_profile(self, payload_profile): + return True + + def supports_document_payload_profile(self, payload_profile): + return True + + def search_documents(self, query, k, payload_profile=None): + return [] + + +class FtsConcurrencyConfig: + num_concurrency = [60, 80] + concurrency_duration = 30 + concurrency_timeout = 300 + serial_cooldown = 0 + + +class FtsCaseConfig: + k = 10 + concurrency_search_config = FtsConcurrencyConfig() + + +def test_fts_search_runners_use_full_queries_for_concurrency_and_filtered_queries_for_recall(): + class Dataset: + queries_data = [ + FtsQuery(query_id="q1", text="full query one"), + FtsQuery(query_id="q2", text="full query two"), + ] + gt_data = [{"d1": 1}, {"d2": 1}] + recall_queries_data = [FtsQuery(query_id="q2", text="full query two")] + recall_gt_data = [{"d2": 1}] + recall_skipped = False + recall_skip_reason = None + + class Case: + label = CaseLabel.FullTextSearchPerformance + dataset = Dataset() + filters = non_filter + payload_profile = PayloadProfile.IDS_ONLY + + config_obj = type( + "Config", + (), + { + "stages": [TaskStage.SEARCH_SERIAL, TaskStage.SEARCH_CONCURRENT], + "case_config": FtsCaseConfig(), + }, + )() + runner = CaseRunner.construct(ca=Case(), config=config_obj, db=DummyDb()) + + runner._init_fts_search_runner() + + assert runner.search_runner.test_data == ["full query one", "full query two"] + assert runner.serial_search_runner.test_data == ["full query two"] + assert runner.serial_search_runner.ground_truth == [{"d2": 1}] + + +def test_fts_perf_metric_marks_recall_skipped_without_blocking_case(monkeypatch): + class Dataset: + filter_stats = { + "filter_field": "filter_id", + "filter_value": 99, + "filtered_query_count": 0, + } + queries_data = [ + FtsQuery(query_id="q1", text="full query one"), + FtsQuery(query_id="q2", text="full query two"), + ] + recall_queries_data = [] + recall_skipped = True + recall_skip_reason = "no_positive_qrels_after_filter" + + class Case: + label = CaseLabel.FullTextSearchPerformance + dataset = Dataset() + + config_obj = type( + "Config", + (), + { + "stages": [TaskStage.SEARCH_SERIAL], + "case_config": FtsCaseConfig(), + }, + )() + runner = CaseRunner.construct(ca=Case(), config=config_obj) + monkeypatch.setattr(CaseRunner, "_init_search_runners", lambda self: None) + + metric = runner._run_perf_case(drop_old=False) + + assert metric.recall == 0.0 + assert metric.ndcg == 0.0 + assert metric.mrr == 0.0 + assert metric.additional_parameters["fts_recall"] == { + "skipped": True, + "reason": "no_positive_qrels_after_filter", + "serial_query_count": 0, + "full_query_count": 2, + } diff --git a/tests/test_fts_format_results.py b/tests/test_fts_format_results.py new file mode 100644 index 000000000..e46c5670a --- /dev/null +++ b/tests/test_fts_format_results.py @@ -0,0 +1,43 @@ +from vectordb_bench.backend.cases import CaseType +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.milvus.config import MilvusConfig, MilvusFtsConfig +from vectordb_bench.metric import Metric +from vectordb_bench.models import CaseConfig, CaseResult, TaskConfig, TestResult +from vectordb_bench.restful.format_res import format_results + + +def test_format_results_supports_fts_dataset_and_metrics(): + test_result = TestResult( + run_id="run-1", + task_label="fts-task", + timestamp=123, + results=[ + CaseResult( + task_config=TaskConfig( + db=DB.Milvus, + db_config=MilvusConfig(uri="http://localhost:19530"), + db_case_config=MilvusFtsConfig(), + case_config=CaseConfig(case_id=CaseType.FTSBm25Performance, k=10), + ), + metrics=Metric( + qps=12.5, + recall=0.7, + ndcg=0.8, + mrr=0.9, + serial_latency_p99=0.11, + serial_latency_p95=0.1, + conc_latency_p99_list=[0.2], + conc_latency_p95_list=[0.15], + conc_latency_avg_list=[0.12], + ), + ) + ], + ) + + [formatted] = format_results([test_result], "fts-task") + + assert formatted["dataset"] == "MS MARCO FTS (SMALL)" + assert formatted["dim"] == 0 + assert formatted["mrr"] == 0.9 + assert formatted["serial_latency_p95"] == 0.1 + assert formatted["conc_latency_p95_list"] == [0.15] diff --git a/tests/test_fts_frontend_results.py b/tests/test_fts_frontend_results.py new file mode 100644 index 000000000..5b802356b --- /dev/null +++ b/tests/test_fts_frontend_results.py @@ -0,0 +1,282 @@ +import json +import tomllib +from inspect import signature +from pathlib import Path +from typing import Any + +import pandas as pd +import streamlit as st + +from vectordb_bench.backend.clients import DB +from vectordb_bench.frontend.config.dbCaseConfigs import get_fts_case_items +from vectordb_bench.frontend.pages.full_text_search import ( + _concurrency_rows, + _draw_filtered_qps_tab, + _peak_filtered_qps_rows, + load_full_text_search_rows, +) + + +def test_oss_opensearch_fts_cases_are_selectable_in_run_test(): + assert all(item.supports_dbs([DB.OSSOpenSearch]) for item in get_fts_case_items()) + + +def test_streamlit_dependency_floor_supports_fts_tab_state_api(): + repo_root = Path(__file__).resolve().parents[1] + dependencies = tomllib.loads((repo_root / "pyproject.toml").read_text())["project"]["dependencies"] + + assert "streamlit>=1.61,<2" in dependencies + assert {"default", "key", "on_change"}.issubset(signature(st.tabs).parameters) + + +def test_frontend_separates_filtered_results_and_expands_concurrency_qps(tmp_path: Path): + result_file = tmp_path / "result_fts.json" + result_file.write_text( + json.dumps( + { + "task_label": "fts-results", + "results": [ + { + "metrics": { + "qps": 100.0, + "recall": 0.9, + "ndcg": 0.8, + "mrr": 0.7, + }, + "task_config": { + "db": "ZillizCloud", + "case_config": { + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + } + }, + }, + }, + { + "metrics": { + "qps": 200.0, + "recall": 0.95, + "ndcg": 0.85, + "mrr": 0.75, + "additional_parameters": { + "fts_filter": { + "filter_rate": 0.5, + "filter_id_distribution": "affine_permutation_v1", + } + }, + "conc_num_list": [60, 80], + "conc_qps_list": [1200.0, 1500.0], + }, + "task_config": { + "db": "ZillizCloud", + "case_config": { + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.5, + } + }, + }, + }, + ], + } + ) + ) + + data = load_full_text_search_rows(tmp_path) + standard_data = data[~data["is_filtered"]] + filtered_data = data[data["is_filtered"]] + concurrency_data = _concurrency_rows(filtered_data) + peak_data = _peak_filtered_qps_rows(filtered_data) + + assert len(standard_data) == 1 + assert standard_data.iloc[0]["qps"] == 100.0 + assert len(filtered_data) == 1 + assert filtered_data.iloc[0]["filter_rate_label"] == "50%" + assert "filter_distribution" not in filtered_data.columns + assert concurrency_data[["concurrency", "qps"]].to_dict("records") == [ + {"concurrency": 60, "qps": 1200.0}, + {"concurrency": 80, "qps": 1500.0}, + ] + assert peak_data[["filter_rate_label", "concurrency", "qps"]].to_dict("records") == [ + {"filter_rate_label": "50%", "concurrency": 80, "qps": 1500.0} + ] + + +def test_checked_in_consolidated_permuted_results_expose_concurrency_qps_for_all_backends(): + repo_root = Path(__file__).resolve().parents[1] + result_dir = repo_root / "vectordb_bench" / "results" / "FullTextSearch" + + data = load_full_text_search_rows(result_dir) + filtered_data = data[data["is_filtered"]] + concurrency_data = _concurrency_rows(filtered_data) + peak_data = _peak_filtered_qps_rows(filtered_data) + + assert len(data) == 64 + assert len(filtered_data) == 40 + assert len(concurrency_data) == 90 + assert (pd.to_numeric(filtered_data["p99_s"]) > 0).all() + assert (pd.to_numeric(filtered_data["p95_s"]) > 0).all() + assert (pd.to_numeric(filtered_data["recall"]) > 0).all() + assert (pd.to_numeric(filtered_data["ndcg"]) > 0).all() + assert (pd.to_numeric(filtered_data["mrr"]) > 0).all() + assert len(peak_data) == 20 + assert set(filtered_data["backend"].astype(str)) == { + "ElasticSearch", + "OSSOpenSearch", + "TurboPuffer", + "ZillizCloud", + } + assert set(concurrency_data["concurrency"]) == {40, 60, 80} + assert set(peak_data["filter_rate_label"]) == {"50%", "75%", "90%", "95%", "99%"} + assert set(peak_data["concurrency"]).issubset({60, 80}) + assert set(pd.to_numeric(filtered_data["filter_rate"])) == {0.5, 0.75, 0.9, 0.95, 0.99} + assert not (result_dir / "ZillizCloud" / "result_20260709_fts_filtered_zillizcloud.json").exists() + + expected_result_files = { + "ElasticCloud": "result_20260626_fts_standard_elasticcloud.json", + "OpenSearch": "result_20260708_fts_standard_opensearch.json", + "TurboPuffer": "result_20260626_fts_standard_turbopuffer.json", + "ZillizCloud": "result_20260626_fts_standard_zillizcloud.json", + } + expected_result_counts = { + "ElasticCloud": 16, + "OpenSearch": 16, + "TurboPuffer": 16, + "ZillizCloud": 16, + } + result_files = sorted(result_dir.glob("*/result_*.json")) + assert len(result_files) == 4 + assert {path.parent.name: path.name for path in result_files} == expected_result_files + + filtered_results = [] + result_counts = {} + for result_file in result_files: + results = json.loads(result_file.read_text())["results"] + result_counts[result_file.parent.name] = len(results) + if result_file.parent.name == "ZillizCloud": + assert all( + "one persistent segment" + in json.loads(case_result["task_config"]["db_config"]["note"])["evidence"]["source"] + for case_result in results + ) + for case_result in results: + custom_case = case_result["task_config"]["case_config"].get("custom_case") or {} + fts_filter = case_result["metrics"].get("additional_parameters", {}).get("fts_filter") or {} + filter_rate = custom_case.get("filter_rate", fts_filter.get("filter_rate")) + if filter_rate is not None: + assert "filter_id_distribution" not in custom_case + assert fts_filter["filter_id_distribution"] == "affine_permutation_v1" + filtered_results.append(case_result) + + assert result_counts == expected_result_counts + assert len(filtered_results) == 40 + expected_serial_fields = { + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr", + } + for case_result in filtered_results: + stages = set(case_result["task_config"]["stages"]) + provenance = case_result["metrics"]["additional_parameters"].get("serial_measurement") + + assert {"search_concurrent", "search_serial"}.issubset(stages) + if provenance is None: + assert case_result["task_config"]["db"] == "ZillizCloud" + assert all(case_result["metrics"][field] > 0 for field in expected_serial_fields) + continue + assert provenance["composed_from_separate_run"] is True + assert set(provenance["metric_fields"]) == expected_serial_fields + assert provenance["source_file"].startswith("result_") + assert len(provenance["source_sha256"]) == 64 + assert "search_serial" in provenance["source_stages"] + assert provenance["source_note"] + + +def test_filtered_qps_chart_groups_backend_bars_by_filter_rate(): + repo_root = Path(__file__).resolve().parents[1] + result_dir = repo_root / "vectordb_bench" / "results" / "FullTextSearch" + data = load_full_text_search_rows(result_dir) + filtered_data = data[data["is_filtered"] & (data["dataset_family"].astype(str) == "MS MARCO")] + + class PlotRecorder: + figure = None + + def info(self, _message: str) -> None: + raise AssertionError("Expected filtered QPS chart data") + + def plotly_chart(self, figure: Any, **_kwargs) -> None: + self.figure = figure + + recorder = PlotRecorder() + _draw_filtered_qps_tab(recorder, filtered_data) + + assert recorder.figure is not None + assert recorder.figure.layout.barmode == "group" + assert {trace.type for trace in recorder.figure.data} == {"bar"} + assert {trace.name for trace in recorder.figure.data} == { + "ElasticSearch", + "OSSOpenSearch", + "TurboPuffer", + "ZillizCloud", + } + expected_filter_rates = {"50%", "75%", "90%", "95%", "99%"} + assert all(set(trace.x) == expected_filter_rates for trace in recorder.figure.data) + assert recorder.figure.layout.xaxis.type == "category" + assert tuple(recorder.figure.layout.xaxis.categoryarray) == ("50%", "75%", "90%", "95%", "99%") + + +def test_checked_in_zilliz_standard_results_use_semantic_metrics(): + repo_root = Path(__file__).resolve().parents[1] + result_dir = repo_root / "vectordb_bench" / "results" / "FullTextSearch" + + data = load_full_text_search_rows(result_dir) + zilliz_data = data[ + (~data["is_filtered"]) & (data["backend"].astype(str) == "ZillizCloud") & (data["payload"] == "ids_only") + ] + expected = { + "MS MARCO Small": (0.9157, 0.7206, 0.6713), + "MS MARCO Medium": (0.8261, 0.5298, 0.4572), + "MS MARCO Large": (0.6283, 0.2763, 0.1889), + "HotpotQA Small": (0.9225, 0.8459, 0.9485), + "HotpotQA Medium": (0.8437, 0.7322, 0.8636), + "HotpotQA Large": (0.7674, 0.6265, 0.7574), + } + + assert len(zilliz_data) == len(expected) + for row in zilliz_data.to_dict("records"): + assert (row["recall"], row["ndcg"], row["mrr"]) == expected[str(row["dataset"])] + + +def test_checked_in_zilliz_large_standard_results_retain_load_metrics(): + repo_root = Path(__file__).resolve().parents[1] + result_file = ( + repo_root + / "vectordb_bench" + / "results" + / "FullTextSearch" + / "ZillizCloud" + / "result_20260626_fts_standard_zillizcloud.json" + ) + expected = { + "HotpotQA Large (5.2M documents)": (5_233_329, 92.5443, 81.9515, 174.4958), + "MS MARCO Large (8.8M documents)": (8_841_823, 183.6571, 98.8113, 282.4684), + } + + large_results = {} + for case_result in json.loads(result_file.read_text())["results"]: + custom_case = case_result["task_config"]["case_config"].get("custom_case") or {} + dataset = custom_case.get("dataset_with_size_type") + if dataset in expected and custom_case.get("filter_rate") is None: + metrics = case_result["metrics"] + large_results[dataset] = ( + metrics["inserted_count"], + metrics["insert_duration"], + metrics["optimize_duration"], + metrics["load_duration"], + ) + + assert large_results == expected diff --git a/tests/test_fts_metrics.py b/tests/test_fts_metrics.py new file mode 100644 index 000000000..fbbb8552a --- /dev/null +++ b/tests/test_fts_metrics.py @@ -0,0 +1,69 @@ +from types import TracebackType +from typing import Any + +import pytest + +from vectordb_bench.backend.payload import PayloadProfile +from vectordb_bench.backend.runner.serial_runner import SerialSearchRunner +from vectordb_bench.backend.workload import WorkloadKind +from vectordb_bench.metric import calc_mrr_fts, calc_ndcg_fts, calc_recall_fts + + +class FakeSearchDB: + name = "FakeSearchDB" + + def init(self): + return self + + def __enter__(self): + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + tb: TracebackType | None, + ) -> bool: + return False + + def prepare_filter(self, filters: Any) -> None: + return None + + def supports_document_payload_profile(self, payload_profile: PayloadProfile) -> bool: + return payload_profile == PayloadProfile.IDS_ONLY + + def supports_payload_profile(self, payload_profile: PayloadProfile) -> bool: + return True + + def search_documents(self, query: str, k: int) -> list[str]: + assert query == "query" + assert k == 3 + return ["missing", "d2", "d1"] + + +def test_semantic_fts_metrics_use_relevance_grades(): + qrels = {"d1": 3, "d2": 1} + got = ["missing", "d2", "d1"] + + assert calc_recall_fts(2, qrels, got) == 0.5 + assert calc_recall_fts(3, qrels, got) == 1.0 + assert calc_mrr_fts(3, qrels, got) == 0.5 + assert calc_ndcg_fts(3, qrels, got) == pytest.approx(0.5869, abs=1e-4) + + +def test_serial_runner_returns_semantic_fts_metrics(): + runner = SerialSearchRunner( + db=FakeSearchDB(), + test_data=["query"], + ground_truth=[{"d1": 3, "d2": 1}], + k=3, + workload_kind=WorkloadKind.FULL_TEXT, + ) + + recall, ndcg, mrr, p99, p95 = runner.search((runner.test_data, runner.ground_truth)) + + assert recall == 1.0 + assert ndcg == 0.5869 + assert mrr == 0.5 + assert p99 >= 0 + assert p95 >= 0 diff --git a/tests/test_lancedb_config.py b/tests/test_lancedb_config.py new file mode 100644 index 000000000..cf2d6e12f --- /dev/null +++ b/tests/test_lancedb_config.py @@ -0,0 +1,280 @@ +"""Offline unit tests for LanceDB config objects. + +These tests don't require a running LanceDB instance. They freeze the +contract for the three IVF-family indexes (IVF_PQ / IVF_HNSW_SQ / +IVF_HNSW_PQ) so that any future refactor that accidentally diverges their +code paths (e.g. introduces index-type-specific branches) will fail CI. + +Background: IVF_HNSW_SQ / IVF_HNSW_PQ share the same code path as IVF_PQ +and the CLI is wired up for all three. These tests encode that claim. +""" + +import typing +from typing import Annotated, get_type_hints + +from vectordb_bench.backend.clients.api import IndexType, MetricType +from vectordb_bench.backend.clients.lancedb.config import ( + LanceDBAutoIndexConfig, + LanceDBIndexConfig, + LanceDBIVFHNSWPQConfig, + LanceDBIVFHNSWSQConfig, + LanceDBNoIndexConfig, + _lancedb_case_config, +) + +# --------------------------------------------------------------------------- +# Registry mapping +# --------------------------------------------------------------------------- + + +def test_registry_covers_all_lancedb_index_types(): + """Every LanceDB-supported IndexType must resolve to a config class.""" + required = { + IndexType.IVFPQ, + IndexType.AUTOINDEX, + IndexType.IVF_HNSW_SQ, + IndexType.IVF_HNSW_PQ, + IndexType.NONE, + } + assert required.issubset(_lancedb_case_config.keys()) + + # HNSW is kept for backwards compatibility and must map to IVF_HNSW_SQ. + assert _lancedb_case_config[IndexType.HNSW] is LanceDBIVFHNSWSQConfig + + +# --------------------------------------------------------------------------- +# index_param() / search_param() contract +# --------------------------------------------------------------------------- + + +def test_ivfpq_default_params_are_minimal(): + cfg = LanceDBIndexConfig() + assert cfg.index == IndexType.IVFPQ + p = cfg.index_param() + # Always present + assert p["metric"] == "cosine" or p["metric"] == "l2" + assert "num_bits" in p + # Zero-valued optionals stay out of the param dict so LanceDB uses its + # own defaults. + assert "num_partitions" not in p + assert "num_sub_vectors" not in p + # search_param() is empty when all tunables are zero. + assert cfg.search_param() == {} + + +def test_ivfpq_tuned_params_are_forwarded(): + cfg = LanceDBIndexConfig( + metric_type=MetricType.COSINE, + num_partitions=256, + num_sub_vectors=96, + nbits=8, + nprobes=20, + refine_factor=10, + ) + p = cfg.index_param() + assert p == { + "metric": "cosine", + "num_bits": 8, + "sample_rate": 256, + "max_iterations": 50, + "num_partitions": 256, + "num_sub_vectors": 96, + } + assert cfg.search_param() == {"nprobes": 20, "refine_factor": 10} + + +def test_ivf_hnsw_sq_params_are_forwarded(): + cfg = LanceDBIVFHNSWSQConfig( + metric_type=MetricType.COSINE, + num_partitions=256, + m=16, + ef_construction=200, + ef=128, + nprobes=20, + refine_factor=10, + ) + p = cfg.index_param() + assert p["index_type"] == "IVF_HNSW_SQ" + assert p["num_partitions"] == 256 + assert p["m"] == 16 + assert p["ef_construction"] == 200 + assert cfg.search_param() == { + "ef": 128, + "nprobes": 20, + "refine_factor": 10, + } + + +def test_ivf_hnsw_pq_params_are_forwarded(): + cfg = LanceDBIVFHNSWPQConfig( + metric_type=MetricType.COSINE, + num_partitions=256, + num_sub_vectors=96, + m=16, + ef_construction=200, + ef=128, + nprobes=20, + refine_factor=10, + ) + p = cfg.index_param() + assert p["index_type"] == "IVF_HNSW_PQ" + assert p["num_partitions"] == 256 + assert p["num_sub_vectors"] == 96 + assert p["m"] == 16 + assert p["ef_construction"] == 200 + assert cfg.search_param() == { + "ef": 128, + "nprobes": 20, + "refine_factor": 10, + } + + +# --------------------------------------------------------------------------- +# Code-path unification +# --------------------------------------------------------------------------- + + +def test_ivf_family_shares_search_knobs(): + """IVF_PQ exposes nprobes+refine_factor; IVF_HNSW_SQ/PQ additionally + expose ef. These are the only keys lancedb.py's search_embedding knows + how to forward, so any new index type must stay inside this union. + """ + allowed = {"nprobes", "ef", "refine_factor"} + + ivfpq = LanceDBIndexConfig(nprobes=10, refine_factor=5) + sq = LanceDBIVFHNSWSQConfig(nprobes=10, ef=64, refine_factor=5) + pq = LanceDBIVFHNSWPQConfig(nprobes=10, ef=64, refine_factor=5) + + for cfg in (ivfpq, sq, pq): + assert set(cfg.search_param().keys()).issubset(allowed) + + +def test_no_index_and_autoindex_are_well_formed(): + none_cfg = LanceDBNoIndexConfig() + assert none_cfg.index == IndexType.NONE + assert none_cfg.index_param() == {} + + auto_cfg = LanceDBAutoIndexConfig() + assert auto_cfg.index == IndexType.AUTOINDEX + assert "metric" in auto_cfg.index_param() + + +# --------------------------------------------------------------------------- +# CLI wiring — validate via static TypedDict introspection (no heavy runtime +# imports needed, avoids hdrh / streamlit / etc. dependency chains) +# --------------------------------------------------------------------------- + + +def _extract_click_option_names(typed_dict_cls: type) -> set[str]: + """Extract ``--option-name`` strings from a Click-annotated TypedDict. + + Each field is ``Annotated[T, click.option("--name", ...)]``. + We pull the option strings from the ``click.Option`` metadata. + """ + hints = get_type_hints(typed_dict_cls, include_extras=True) + names: set[str] = set() + for _field, hint in hints.items(): + if typing.get_origin(hint) is Annotated: + for meta in hint.__metadata__: + # click.option(...) produces a click.core.Decorator / functools.partial + # but in this project it's stored as a click.Argument or a + # ``functools.partial`` wrapping ``click.option``. Extract the + # first positional string that starts with "--". + if hasattr(meta, "name") and isinstance(meta.name, str): + names.add(meta.name) + # click.option() returns a decorator whose .args[0] is the + # option flag(s). We try a few accessor patterns. + for attr in ("args", "decls"): + for val in getattr(meta, attr, ()): + if isinstance(val, str) and val.startswith("--"): + names.add(val) + # For click.option stored as click.core.Option or Decorator + if hasattr(meta, "opts"): + for opt in meta.opts: + if isinstance(opt, str) and opt.startswith("--"): + names.add(opt) + return names + + +def test_cli_typed_dicts_define_all_expected_commands(): + """Every expected CLI TypedDict class must be importable from lancedb/cli.py.""" + # We mock the heavy module to avoid pulling the entire runtime. + # lancedb/cli.py imports ....cli.cli which triggers hdrh etc. + # Instead we just verify the TypedDict definitions exist in source. + import ast + from pathlib import Path + + cli_path = Path("vectordb_bench/backend/clients/lancedb/cli.py") + tree = ast.parse(cli_path.read_text()) + + class_names: set[str] = set() + func_names: set[str] = set() + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef): + class_names.add(node.name) + elif isinstance(node, ast.FunctionDef): + func_names.add(node.name) + + # TypedDicts that drive CLI options + assert { + "LanceDBTypedDict", + "LanceDBIVFPQTypedDict", + "LanceDBIVFHNSWSQTypedDict", + "LanceDBIVFHNSWPQTypedDict", + }.issubset(class_names) + + # Command functions registered via @cli.command() + assert {"LanceDB", "LanceDBAutoIndex", "LanceDBIVFPQ", "LanceDBIVFHNSWSQ", "LanceDBIVFHNSWPQ"}.issubset(func_names) + + +def test_cli_typeddict_ivfpq_has_search_knobs(): + """IVF_PQ TypedDict must define nprobes / refine_factor / num_partitions.""" + import ast + from pathlib import Path + + cli_src = Path("vectordb_bench/backend/clients/lancedb/cli.py").read_text() + tree = ast.parse(cli_src) + + def _fields_of(cls_name: str) -> set[str]: + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef) and node.name == cls_name: + return { + item.target.id + for item in node.body + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name) + } + return set() + + ivfpq = _fields_of("LanceDBIVFPQTypedDict") + assert {"nprobes", "refine_factor", "num_partitions", "num_sub_vectors", "nbits"}.issubset(ivfpq) + + +def test_cli_typeddict_hnsw_variants_are_superset_of_ivfpq(): + """Both HNSW TypedDicts must have nprobes + refine_factor + graph knobs.""" + import ast + from pathlib import Path + + cli_src = Path("vectordb_bench/backend/clients/lancedb/cli.py").read_text() + tree = ast.parse(cli_src) + + def _fields_of(cls_name: str) -> set[str]: + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef) and node.name == cls_name: + return { + item.target.id + for item in node.body + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name) + } + return set() + + sq_fields = _fields_of("LanceDBIVFHNSWSQTypedDict") + pq_fields = _fields_of("LanceDBIVFHNSWPQTypedDict") + + # Both must have shared search knobs + shared_knobs = {"nprobes", "refine_factor", "num_partitions", "m", "ef", "ef_construction"} + assert shared_knobs.issubset(sq_fields), f"SQ missing: {shared_knobs - sq_fields}" + assert shared_knobs.issubset(pq_fields), f"PQ missing: {shared_knobs - pq_fields}" + + # PQ variant has num_sub_vectors, SQ does not + assert "num_sub_vectors" in pq_fields + assert "num_sub_vectors" not in sq_fields diff --git a/tests/test_milvus.py b/tests/test_milvus.py new file mode 100644 index 000000000..ef131ef87 --- /dev/null +++ b/tests/test_milvus.py @@ -0,0 +1,467 @@ +"""E2E test for Milvus client using MilvusClient API. + +Requires a running Milvus instance at localhost:19530. +""" + +import logging +from types import SimpleNamespace +from unittest.mock import MagicMock, call + +import pytest +from pydantic import SecretStr + +from vectordb_bench.backend.cases import CaseType +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.api import IndexType +from vectordb_bench.backend.clients.milvus.config import MilvusConfig +from vectordb_bench.backend.clients.milvus.milvus import MILVUS_FORCE_MERGE_TARGET_SIZE_MB, Milvus +from vectordb_bench.backend.payload import PayloadProfile +from vectordb_bench.interface import BenchMarkRunner +from vectordb_bench.models import CaseConfig, TaskConfig + +log = logging.getLogger(__name__) + + +class TestMilvusOptimize: + def _milvus( + self, + *, + compact_side_effect: Exception | None = None, + is_fts: bool = False, + is_gpu_index: bool = False, + ): + milvus = Milvus.__new__(Milvus) + milvus.name = "Milvus" + milvus.collection_name = "test_collection" + milvus._is_fts = is_fts + milvus.case_config = SimpleNamespace(is_gpu_index=is_gpu_index) + milvus.client = MagicMock() + milvus.client.compact.side_effect = compact_side_effect + milvus.client.compact.return_value = 42 + milvus._wait_for_segments_sorted = MagicMock() + milvus._wait_for_index = MagicMock() + milvus._wait_for_compaction = MagicMock() + return milvus + + def test_optimize_compact_uses_safe_force_merge_target_size(self): + milvus = self._milvus() + + milvus._optimize() + + milvus.client.compact.assert_any_call("test_collection", target_size=MILVUS_FORCE_MERGE_TARGET_SIZE_MB) + milvus.client.refresh_load.assert_called_once_with("test_collection") + + def test_optimize_compacts_fts_collections(self): + milvus = self._milvus(is_fts=True) + + milvus._optimize() + + milvus.client.compact.assert_any_call("test_collection", target_size=MILVUS_FORCE_MERGE_TARGET_SIZE_MB) + milvus.client.refresh_load.assert_called_once_with("test_collection") + + def test_optimize_flushes_and_runs_normal_compaction_before_force_merge(self): + milvus = self._milvus(compact_side_effect=[41, 42]) + + milvus._optimize() + + milvus.client.flush.assert_called_once_with("test_collection") + assert milvus.client.compact.call_args_list == [ + call("test_collection"), + call("test_collection", target_size=MILVUS_FORCE_MERGE_TARGET_SIZE_MB), + ] + assert milvus._wait_for_segments_sorted.call_count == 2 + assert milvus._wait_for_index.call_count == 3 + assert milvus._wait_for_compaction.call_args_list == [call(41), call(42)] + milvus.client.refresh_load.assert_called_once_with("test_collection") + + def test_optimize_retries_when_compacting_segments_are_missing_from_force_merge_plan( + self, monkeypatch: pytest.MonkeyPatch + ): + class FakeMilvusClient: + def __init__(self): + self.segment_ids = [1, 2, 3] + self.force_merge_completed = False + + def flush(self, _collection_name: str): + pass + + def list_persistent_segments(self, _collection_name: str): + return [ + SimpleNamespace( + segment_id=segment_id, + is_sorted=True, + state_name="Flushed", + level_name="L1", + ) + for segment_id in self.segment_ids + ] + + def describe_index(self, _collection_name: str, _index_name: str): + return {"pending_index_rows": 0} + + def compact(self, _collection_name: str, *, target_size: int | None = None): + if target_size is None: + return 90 + assert target_size == MILVUS_FORCE_MERGE_TARGET_SIZE_MB + if self.segment_ids == [1, 2, 3]: + return 100 + if self.segment_ids == [10, 20]: + return 101 + message = f"unexpected force merge input: {self.segment_ids}" + raise AssertionError(message) + + def get_compaction_state(self, compaction_id: int): + if compaction_id == 90: + pass + elif compaction_id == 100: + self.segment_ids = [10, 20] + elif compaction_id == 101: + self.segment_ids = [30] + self.force_merge_completed = True + else: + message = f"unexpected compaction id: {compaction_id}" + raise AssertionError(message) + return "Completed" + + def get_compaction_plans(self, compaction_id: int): + sources = [1] if compaction_id == 100 else [10, 20] + return SimpleNamespace(plans=[SimpleNamespace(sources=sources)]) + + def refresh_load(self, _collection_name: str): + assert self.force_merge_completed, "refresh started after only a partial force merge" + + milvus = Milvus.__new__(Milvus) + milvus.name = "Milvus" + milvus.collection_name = "test_collection" + milvus._is_fts = False + milvus._main_index_name = "vector_idx" + milvus.case_config = SimpleNamespace(is_gpu_index=False) + milvus.client = FakeMilvusClient() + monkeypatch.setattr("vectordb_bench.backend.clients.milvus.milvus.time.sleep", lambda _seconds: None) + + milvus.optimize(data_size=500_000) + + assert milvus.client.force_merge_completed + + def test_optimize_retries_when_all_segments_are_compacting(self, monkeypatch: pytest.MonkeyPatch): + class FakeMilvusClient: + def __init__(self): + self.segment_ids = [1, 2] + self.compact_attempts = 0 + self.force_merge_completed = False + + def flush(self, _collection_name: str): + pass + + def list_persistent_segments(self, _collection_name: str): + return [ + SimpleNamespace( + segment_id=segment_id, + is_sorted=True, + state_name="Flushed", + level_name="L1", + ) + for segment_id in self.segment_ids + ] + + def describe_index(self, _collection_name: str, _index_name: str): + return {"pending_index_rows": 0} + + def compact(self, _collection_name: str, *, target_size: int | None = None): + if target_size is None: + return 90 + assert target_size == MILVUS_FORCE_MERGE_TARGET_SIZE_MB + self.compact_attempts += 1 + if self.compact_attempts == 1: + self.segment_ids = [10] + return -1 + return 101 + + def get_compaction_state(self, compaction_id: int): + if compaction_id == 90: + return "Completed" + assert compaction_id == 101 + self.segment_ids = [20] + self.force_merge_completed = True + return "Completed" + + def get_compaction_plans(self, compaction_id: int): + assert compaction_id == 101 + return SimpleNamespace(plans=[SimpleNamespace(sources=[10])]) + + def refresh_load(self, _collection_name: str): + assert self.force_merge_completed, "refresh started without a force merge job" + + milvus = Milvus.__new__(Milvus) + milvus.name = "Milvus" + milvus.collection_name = "test_collection" + milvus._is_fts = False + milvus._main_index_name = "vector_idx" + milvus.case_config = SimpleNamespace(is_gpu_index=False) + milvus.client = FakeMilvusClient() + monkeypatch.setattr("vectordb_bench.backend.clients.milvus.milvus.time.sleep", lambda _seconds: None) + + milvus.optimize(data_size=500_000) + + assert milvus.client.force_merge_completed + + def test_force_merge_uses_fresh_snapshots_and_stops_after_max_attempts(self, monkeypatch: pytest.MonkeyPatch): + class FakeMilvusClient: + def __init__(self): + self.segment_ids = [1, 2] + self.compact_attempts = 0 + + def list_persistent_segments(self, _collection_name: str): + return [ + SimpleNamespace( + segment_id=segment_id, + is_sorted=True, + state_name="Flushed", + level_name="L1", + ) + for segment_id in self.segment_ids + ] + + def describe_index(self, _collection_name: str, _index_name: str): + return {"pending_index_rows": 0} + + def compact(self, _collection_name: str, *, target_size: int): + assert target_size == MILVUS_FORCE_MERGE_TARGET_SIZE_MB + self.compact_attempts += 1 + return 100 + self.compact_attempts + + def get_compaction_state(self, _compaction_id: int): + next_segment_ids = { + 1: [10, 20], + 2: [30, 40], + 3: [50, 60], + } + self.segment_ids = next_segment_ids[self.compact_attempts] + return "Completed" + + def get_compaction_plans(self, _compaction_id: int): + planned_source = {1: 1, 2: 10, 3: 30}[self.compact_attempts] + return SimpleNamespace(plans=[SimpleNamespace(sources=[planned_source])]) + + milvus = Milvus.__new__(Milvus) + milvus.name = "Milvus" + milvus.collection_name = "test_collection" + milvus._main_index_name = "vector_idx" + milvus.client = FakeMilvusClient() + monkeypatch.setattr("vectordb_bench.backend.clients.milvus.milvus.time.sleep", lambda _seconds: None) + + with pytest.raises(RuntimeError, match=r"after 3 attempts.*missing segments: \[40\]"): + milvus._force_merge(max_attempts=3) + + assert milvus.client.compact_attempts == 3 + + def test_optimize_skips_gpu_index_compaction(self): + milvus = self._milvus(is_gpu_index=True) + + milvus._optimize() + + milvus.client.compact.assert_not_called() + milvus.client.refresh_load.assert_called_once_with("test_collection") + + def test_optimize_skips_property_style_permission_denied(self): + error = RuntimeError("permission denied") + error.code = SimpleNamespace(name="PERMISSION_DENIED") + milvus = self._milvus(compact_side_effect=error) + + milvus._optimize() + + milvus.client.refresh_load.assert_called_once_with("test_collection") + + def test_optimize_reraises_non_permission_error(self): + error = RuntimeError("boom") + error.code = SimpleNamespace(name="UNAVAILABLE") + milvus = self._milvus(compact_side_effect=error) + + with pytest.raises(RuntimeError, match="boom") as exc_info: + milvus._optimize() + + assert exc_info.value is error + milvus.client.refresh_load.assert_not_called() + + +@pytest.mark.integration +class TestMilvus: + """E2E test for Milvus using Performance1536D50K (OpenAI 50K dataset).""" + + def test_performance_1536d_50k(self): + """Full benchmark: download dataset, insert, optimize (force merge), search.""" + runner = BenchMarkRunner() + + task_config = TaskConfig( + db=DB.Milvus, + db_config=MilvusConfig(uri=SecretStr("http://localhost:19530")), + db_case_config=DB.Milvus.case_config_cls(index_type=IndexType.Flat)(), + case_config=CaseConfig(case_id=CaseType.Performance1536D50K), + ) + + runner.run([task_config]) + runner._sync_running_task() + result = runner.get_results() + log.info(f"test result: {result}") + assert len(result) > 0 + + +def test_milvus_multitenant_search_uses_tenant_label_filter(): + captured = {} + + def search(**kwargs): + captured.update(kwargs) + return [[{"pk": 1}]] + + db = object.__new__(Milvus) + db.client = SimpleNamespace(search=search) + db.collection_name = "test_collection" + db._vector_field = "vector" + db._primary_field = "pk" + db._scalar_label_field = "label" + db.case_config = SimpleNamespace(search_param=lambda: {"metric_type": "COSINE"}) + db.expr = "" + + result = db.search_embedding([0.1, 0.2], k=3, payload_profile=PayloadProfile.IDS_ONLY, tenant="tenant_0003") + + assert result == [1] + assert captured["filter"] == "label == 'tenant_0003'" + + +def test_milvus_validate_multitenant_schema_accepts_partition_key_label( + monkeypatch: pytest.MonkeyPatch, +) -> None: + closed = [] + + class FakeMilvusClient: + def __init__(self, **_kwargs: object) -> None: + pass + + def describe_collection(self, _collection_name: str) -> dict: + return { + "fields": [ + {"name": "pk", "is_primary": True}, + {"name": "label", "is_partition_key": True}, + ] + } + + def close(self) -> None: + closed.append(True) + + monkeypatch.setattr("vectordb_bench.backend.clients.milvus.milvus.MilvusClient", FakeMilvusClient) + + db = object.__new__(Milvus) + db.name = "Milvus" + db.db_config = {"uri": "http://example.invalid", "user": None, "password": None, "token": ""} + db.collection_name = "existing" + db._scalar_label_field = "label" + + db.validate_multitenant_schema() + + assert closed == [True] + + +def test_milvus_validate_multitenant_schema_rejects_non_partition_key_label( + monkeypatch: pytest.MonkeyPatch, +) -> None: + class FakeMilvusClient: + def __init__(self, **_kwargs: object) -> None: + pass + + def describe_collection(self, _collection_name: str) -> dict: + return {"fields": [{"name": "label", "is_partition_key": False}]} + + def close(self) -> None: + pass + + monkeypatch.setattr("vectordb_bench.backend.clients.milvus.milvus.MilvusClient", FakeMilvusClient) + + db = object.__new__(Milvus) + db.name = "Milvus" + db.db_config = {"uri": "http://example.invalid", "user": None, "password": None, "token": ""} + db.collection_name = "existing" + db._scalar_label_field = "label" + + with pytest.raises(ValueError, match="label field is not a partition key"): + db.validate_multitenant_schema() + + +def test_milvus_validate_multitenant_schema_uses_existing_labels_partition_key( + monkeypatch: pytest.MonkeyPatch, +) -> None: + captured = {} + + class FakeMilvusClient: + def __init__(self, **_kwargs: object) -> None: + pass + + def describe_collection(self, _collection_name: str) -> dict: + return { + "fields": [ + {"name": "pk", "is_primary": True}, + {"name": "labels", "is_partition_key": True}, + {"name": "scalar_label", "nullable": True}, + ] + } + + def close(self) -> None: + pass + + def search(**kwargs): + captured.update(kwargs) + return [[{"pk": 1}]] + + monkeypatch.setattr("vectordb_bench.backend.clients.milvus.milvus.MilvusClient", FakeMilvusClient) + + db = object.__new__(Milvus) + db.name = "Milvus" + db.db_config = {"uri": "http://example.invalid", "user": None, "password": None, "token": ""} + db.collection_name = "existing" + db._vector_field = "vector" + db._primary_field = "pk" + db._scalar_label_field = "label" + db.case_config = SimpleNamespace(search_param=lambda: {"metric_type": "COSINE"}) + db.expr = "" + + db.validate_multitenant_schema() + db.client = SimpleNamespace(search=search) + + db.search_embedding([0.1, 0.2], payload_profile=PayloadProfile.SCALAR_LABEL, tenant="tenant_0003") + + assert captured["filter"] == "labels == 'tenant_0003'" + assert captured["output_fields"] == ["scalar_label"] + + +def test_milvus_multitenant_insert_writes_tenant_and_scalar_payload_labels() -> None: + inserted = {} + + def insert(collection_name, batch_data): + inserted["collection_name"] = collection_name + inserted["batch_data"] = batch_data + return {"insert_count": len(batch_data)} + + db = object.__new__(Milvus) + db.client = SimpleNamespace(insert=insert) + db.collection_name = "test_collection" + db.batch_size = 100 + db._primary_field = "pk" + db._scalar_id_field = "id" + db._vector_field = "vector" + db._scalar_label_field = "label" + db._scalar_payload_label_field = "scalar_label" + db._multitenant_partition_key_field = "labels" + db.with_scalar_labels = True + + count, err = db.insert_embeddings( + embeddings=[[0.1, 0.2], [0.3, 0.4]], + metadata=[1, 2], + labels_data=["label_a", "label_b"], + tenant_labels_data=["tenant_0001", "tenant_0002"], + ) + + assert count == 2 + assert err is None + assert inserted["batch_data"] == [ + {"pk": 1, "id": 1, "vector": [0.1, 0.2], "labels": "tenant_0001", "scalar_label": "label_a"}, + {"pk": 2, "id": 2, "vector": [0.3, 0.4], "labels": "tenant_0002", "scalar_label": "label_b"}, + ] diff --git a/tests/test_milvus_zilliz_cli.py b/tests/test_milvus_zilliz_cli.py new file mode 100644 index 000000000..4177c8898 --- /dev/null +++ b/tests/test_milvus_zilliz_cli.py @@ -0,0 +1,82 @@ +from click.testing import CliRunner +from pytest import MonkeyPatch + +from vectordb_bench.backend.clients.milvus import cli as milvus_cli +from vectordb_bench.backend.clients.zilliz_cloud import cli as zilliz_cli + + +def test_milvus_cli_builds_shared_connection_config() -> None: + parameters = { + "db_label": "milvus-test", + "uri": "http://localhost:19530", + "user_name": "root", + "password": "secret", + "num_shards": "2", + "replica_number": "3", + "collection_name": "bench_collection", + } + + config = milvus_cli._build_milvus_config(parameters) + + assert config.db_label == "milvus-test" + assert config.uri.get_secret_value() == "http://localhost:19530" + assert config.user == "root" + assert config.password.get_secret_value() == "secret" + assert config.num_shards == 2 + assert config.replica_number == 3 + assert config.collection_name == "bench_collection" + + parameters["password"] = None + assert milvus_cli._build_milvus_config(parameters).password is None + + +def test_milvus_autoindex_cli_enables_partition_key_for_multitenant_case( + monkeypatch: MonkeyPatch, +) -> None: + captured = {} + + def fake_run(**kwargs): + captured.update(kwargs) + + monkeypatch.setattr(milvus_cli, "run", fake_run) + + result = CliRunner().invoke( + milvus_cli.MilvusAutoIndex, + [ + "--case-type", + "CloudMultiTenantSearchCase", + "--uri", + "http://localhost:19530", + "--dry-run", + ], + ) + + assert result.exit_code == 0, result.output + assert captured["db_case_config"].use_partition_key is True + + +def test_zilliz_autoindex_cli_enables_partition_key_for_multitenant_case( + monkeypatch: MonkeyPatch, +) -> None: + captured = {} + + def fake_run(**kwargs): + captured.update(kwargs) + + monkeypatch.setattr(zilliz_cli, "run", fake_run) + + result = CliRunner().invoke( + zilliz_cli.ZillizAutoIndex, + [ + "--case-type", + "CloudMultiTenantSearchCase", + "--uri", + "https://example.api.gcp-us-west1.zillizcloud.com", + "--token", + "secret", + "--dry-run", + ], + ) + + assert result.exit_code == 0, result.output + assert captured["db_case_config"].use_partition_key is True diff --git a/tests/test_multitenant_case.py b/tests/test_multitenant_case.py new file mode 100644 index 000000000..45939c32a --- /dev/null +++ b/tests/test_multitenant_case.py @@ -0,0 +1,400 @@ +from contextlib import contextmanager +from types import SimpleNamespace + +import pytest + +from vectordb_bench.backend.cases import CaseType, CloudMultiTenantSearchCase +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.api import EmptyDBCaseConfig, VectorDB +from vectordb_bench.backend.clients.zilliz_cloud.config import AutoIndexConfig, ZillizCloudConfig +from vectordb_bench.backend.data_source import DatasetSource +from vectordb_bench.backend.dataset import DatasetManager, DatasetWithSizeType +from vectordb_bench.backend.filter import FilterOp +from vectordb_bench.backend.payload import PayloadProfile +from vectordb_bench.backend.task_runner import CaseRunner, RunningStatus +from vectordb_bench.models import CaseConfig, TaskConfig, TaskStage + + +def test_multitenant_case_defaults_to_cohere_large_1000_tenants(): + case = CloudMultiTenantSearchCase() + + assert case.case_id == CaseType.CloudMultiTenantSearchCase + assert case.dataset_with_size_type == DatasetWithSizeType.CohereLarge + assert case.dataset.data.size == 10_000_000 + assert case.tenant_count == 1000 + assert case.tenant_prefix == "tenant_" + assert case.tenant_id_width == 4 + assert case.measure_recall is False + assert case.is_multitenant is True + assert case.filters.type == FilterOp.NonFilter + + +def test_multitenant_case_accepts_dataset_and_tenant_count(): + case = CloudMultiTenantSearchCase( + dataset_with_size_type=DatasetWithSizeType.CohereSmall.value, + tenant_count=7, + tenant_prefix="acct_", + tenant_id_width=2, + payload_profile=PayloadProfile.VECTOR.value, + filter_rate=0.01, + ) + + assert case.dataset_with_size_type == DatasetWithSizeType.CohereSmall + assert case.dataset.data.size == 100_000 + assert case.payload_profile == PayloadProfile.VECTOR + assert case.estimated_payload_bytes_per_query(k=10) == PayloadProfile.VECTOR.estimated_bytes_per_query( + k=10, + dim=case.dataset.data.dim, + ) + assert case.filters.type == FilterOp.NumGE + assert case.tenant_count == 7 + assert case.tenant_for_id(0) == "acct_00" + assert case.tenant_for_id(8) == "acct_01" + assert case.tenant_labels_for_ids([0, 1, 8, 13]) == ["acct_00", "acct_01", "acct_01", "acct_06"] + + +def test_case_config_constructs_multitenant_case(): + case = CaseType.CloudMultiTenantSearchCase.case_cls( + { + "dataset_with_size_type": DatasetWithSizeType.CohereSmall.value, + "tenant_count": 5, + "payload_profile": PayloadProfile.SCALAR_LABEL.value, + "label_percentage": 0.01, + } + ) + + assert isinstance(case, CloudMultiTenantSearchCase) + assert case.payload_profile == PayloadProfile.SCALAR_LABEL + assert case.filters.type == FilterOp.StrEqual + assert case.tenant_labels() == ["tenant_0000", "tenant_0001", "tenant_0002", "tenant_0003", "tenant_0004"] + + +class TenantApiProbeDB(VectorDB): + name = "TenantApiProbeDB" + + def __init__(self, dim=2, db_config=None, db_case_config=None, collection_name="test", drop_old=False, **kwargs): + self.insert_calls = [] + self.search_calls = [] + + @contextmanager + def init(self): + yield + + def insert_embeddings(self, embeddings, metadata, labels_data=None, tenant_labels_data=None, **kwargs): + self.insert_calls.append((embeddings, metadata, labels_data, tenant_labels_data)) + return len(embeddings), None + + def search_embedding(self, query, k=100, payload_profile=None, tenant=None): + self.search_calls.append((query, k, payload_profile, tenant)) + return [] + + def optimize(self, data_size=None): + return None + + +def test_vector_db_accepts_optional_tenant_context(): + db = TenantApiProbeDB(db_case_config=EmptyDBCaseConfig()) + + count, err = db.insert_embeddings([[0.1, 0.2]], [42], tenant_labels_data=["tenant_0002"]) + result = db.search_embedding([0.1, 0.2], tenant="tenant_0002") + + assert count == 1 + assert err is None + assert result == [] + assert db.insert_calls[0][3] == ["tenant_0002"] + assert db.search_calls[0][3] == "tenant_0002" + + +def test_search_only_zilliz_multitenant_validates_existing_partition_key_schema( + monkeypatch: pytest.MonkeyPatch, +) -> None: + case = CloudMultiTenantSearchCase() + task = TaskConfig( + db=DB.ZillizCloud, + db_config=ZillizCloudConfig(uri="http://example.invalid", collection_name="existing"), + db_case_config=AutoIndexConfig(use_partition_key=False), + case_config=CaseConfig(case_id=CaseType.CloudMultiTenantSearchCase), + stages=[TaskStage.SEARCH_CONCURRENT], + ) + runner = CaseRunner( + run_id="run-id", + config=task, + ca=case, + status=RunningStatus.PENDING, + dataset_source=DatasetSource.S3, + ) + calls: list[tuple[str, object]] = [] + + class ExistingCollectionDB: + def supports_multitenant(self) -> bool: + return True + + def set_multitenant_context(self, tenant_labels: list[str]) -> None: + calls.append(("set_context", tenant_labels)) + + def validate_multitenant_schema(self) -> None: + calls.append(("validate_schema", None)) + + def fake_init_db(self: CaseRunner, _drop_old: bool = True) -> None: + self.db = ExistingCollectionDB() + + def fake_prepare(*_args: object, **_kwargs: object) -> None: + calls.append(("prepare", None)) + + monkeypatch.setattr(CaseRunner, "init_db", fake_init_db) + monkeypatch.setattr(DatasetManager, "prepare", fake_prepare) + + runner._pre_run(drop_old=False) + + assert ("validate_schema", None) in calls + assert calls[0][0] == "set_context" + + +def test_zilliz_multitenant_create_still_requires_partition_key() -> None: + case = CloudMultiTenantSearchCase() + task = TaskConfig( + db=DB.ZillizCloud, + db_config=ZillizCloudConfig(uri="http://example.invalid", collection_name="new_collection"), + db_case_config=AutoIndexConfig(use_partition_key=False), + case_config=CaseConfig(case_id=CaseType.CloudMultiTenantSearchCase), + stages=[TaskStage.DROP_OLD, TaskStage.LOAD], + ) + runner = CaseRunner( + run_id="run-id", + config=task, + ca=case, + status=RunningStatus.PENDING, + dataset_source=DatasetSource.S3, + ) + + with pytest.raises(ValueError, match="requires use_partition_key=True"): + runner._pre_run(drop_old=True) + + +class FakeTurboNamespace: + def __init__(self): + self.write_calls = [] + self.query_calls = [] + + def write(self, **kwargs): + self.write_calls.append(kwargs) + + def query(self, **kwargs): + self.query_calls.append(kwargs) + return SimpleNamespace(rows=[SimpleNamespace(id="10")]) + + def metadata(self): + return {"index": {"unindexed_bytes": 0}} + + +class FakeTurboClient: + def __init__(self): + self.namespaces = {} + + def namespace(self, name): + self.namespaces.setdefault(name, FakeTurboNamespace()) + return self.namespaces[name] + + +def test_turbopuffer_groups_multitenant_insert_and_search(monkeypatch): + from vectordb_bench.backend.clients.api import MetricType + from vectordb_bench.backend.clients.turbopuffer.config import TurboPufferIndexConfig + from vectordb_bench.backend.clients.turbopuffer.turbopuffer import TurboPuffer + + fake_client = FakeTurboClient() + monkeypatch.setattr(TurboPuffer, "_create_client", lambda self: fake_client) + + db = TurboPuffer( + dim=2, + db_config={ + "api_key": "k", + "region": "r", + "api_base_url": None, + "namespace": "single", + "multitenant_namespace_prefix": "mt_", + }, + db_case_config=TurboPufferIndexConfig(metric_type=MetricType.COSINE), + drop_old=False, + ) + db.set_multitenant_context(["tenant_0000", "tenant_0001"]) + + with db.init(): + count, err = db.insert_embeddings( + embeddings=[[1.0, 0.0], [0.0, 1.0], [0.5, 0.5]], + metadata=[0, 1, 2], + tenant_labels_data=["tenant_0000", "tenant_0001", "tenant_0000"], + ) + result = db.search_embedding([1.0, 0.0], k=1, tenant="tenant_0001") + + assert count == 3 + assert err is None + assert result == [10] + assert fake_client.namespaces["mt_tenant_0000"].write_calls[0]["upsert_columns"]["id"] == [0, 2] + assert fake_client.namespaces["mt_tenant_0001"].write_calls[0]["upsert_columns"]["id"] == [1] + assert fake_client.namespaces["mt_tenant_0001"].query_calls + + +def test_turbopuffer_multitenant_insert_preserves_scalar_payload_labels(monkeypatch): + from vectordb_bench.backend.clients.api import MetricType + from vectordb_bench.backend.clients.turbopuffer.config import TurboPufferIndexConfig + from vectordb_bench.backend.clients.turbopuffer.turbopuffer import TurboPuffer + + fake_client = FakeTurboClient() + monkeypatch.setattr(TurboPuffer, "_create_client", lambda self: fake_client) + + db = TurboPuffer( + dim=2, + db_config={ + "api_key": "k", + "region": "r", + "api_base_url": None, + "namespace": "single", + "multitenant_namespace_prefix": "mt_", + "scalar_payload_label_field": "scalar_label", + }, + db_case_config=TurboPufferIndexConfig(metric_type=MetricType.COSINE), + drop_old=False, + with_scalar_labels=True, + ) + + with db.init(): + count, err = db.insert_embeddings( + embeddings=[[1.0, 0.0], [0.0, 1.0]], + metadata=[0, 1], + labels_data=["label_a", "label_b"], + tenant_labels_data=["tenant_0000", "tenant_0001"], + ) + + assert count == 2 + assert err is None + assert fake_client.namespaces["mt_tenant_0000"].write_calls[0]["upsert_columns"]["scalar_label"] == ["label_a"] + assert fake_client.namespaces["mt_tenant_0001"].write_calls[0]["upsert_columns"]["scalar_label"] == ["label_b"] + + +def test_turbopuffer_multitenant_partial_insert_failure_is_explicit(monkeypatch): + from vectordb_bench.backend.clients.api import MetricType + from vectordb_bench.backend.clients.turbopuffer.config import TurboPufferIndexConfig + from vectordb_bench.backend.clients.turbopuffer.turbopuffer import TurboPuffer + + class FailingNamespace(FakeTurboNamespace): + def __init__(self, fail_write: bool = False): + super().__init__() + self.fail_write = fail_write + + def write(self, **kwargs): + self.write_calls.append(kwargs) + if self.fail_write: + raise RuntimeError("tenant write failed") + + class FailingTurboClient: + def __init__(self): + self.namespaces = {} + + def namespace(self, name): + self.namespaces.setdefault(name, FailingNamespace(fail_write=name == "mt_tenant_0001")) + return self.namespaces[name] + + fake_client = FailingTurboClient() + monkeypatch.setattr(TurboPuffer, "_create_client", lambda self: fake_client) + + db = TurboPuffer( + dim=2, + db_config={ + "api_key": "k", + "region": "r", + "api_base_url": None, + "namespace": "single", + "multitenant_namespace_prefix": "mt_", + }, + db_case_config=TurboPufferIndexConfig(metric_type=MetricType.COSINE), + drop_old=False, + ) + + with db.init(): + count, err = db.insert_embeddings( + embeddings=[[1.0, 0.0], [0.0, 1.0], [0.5, 0.5]], + metadata=[0, 1, 2], + tenant_labels_data=["tenant_0000", "tenant_0001", "tenant_0000"], + ) + + assert count == 2 + assert getattr(err, "non_retryable", False) is True + assert getattr(err, "inserted_count") == 2 + assert getattr(err, "successful_tenants") == {"tenant_0000": 2} + assert getattr(err, "failed_tenant") == "tenant_0001" + assert "tenant_0001" in str(err) + assert fake_client.namespaces["mt_tenant_0000"].write_calls + assert fake_client.namespaces["mt_tenant_0001"].write_calls + + +def test_turbopuffer_pins_multitenant_namespaces_on_init(monkeypatch): + from vectordb_bench.backend.clients.api import MetricType + from vectordb_bench.backend.clients.turbopuffer import turbopuffer as turbopuffer_module + from vectordb_bench.backend.clients.turbopuffer.config import TurboPufferIndexConfig + from vectordb_bench.backend.clients.turbopuffer.turbopuffer import TurboPuffer + + fake_client = FakeTurboClient() + calls = [] + + def fake_metadata_request(api_key, region, namespace, method, payload=None, api_base_url=None): + calls.append((method, namespace, payload, api_base_url)) + return {} + + def fake_wait_for_pinning(api_key, region, namespace, replicas, api_base_url=None, timeout=None): + calls.append(("WAIT", namespace, replicas, timeout)) + return {"pinning": {"replicas": replicas, "status": {"ready_replicas": replicas}}} + + monkeypatch.setattr(TurboPuffer, "_create_client", lambda self: fake_client) + monkeypatch.setattr(turbopuffer_module, "namespace_metadata_request", fake_metadata_request) + monkeypatch.setattr(turbopuffer_module, "wait_for_namespace_pinning", fake_wait_for_pinning) + + db = TurboPuffer( + dim=2, + db_config={ + "api_key": "k", + "region": "r", + "api_base_url": "https://tpuf.example", + "namespace": "single", + "multitenant_namespace_prefix": "mt_", + "pin_namespace": True, + "pin_replicas": 2, + "pin_timeout": 30, + }, + db_case_config=TurboPufferIndexConfig(metric_type=MetricType.COSINE), + drop_old=False, + ) + db.set_multitenant_context(["tenant_0000", "tenant_0001"]) + + with db.init(): + pass + + assert calls == [ + ("PATCH", "mt_tenant_0000", {"pinning": {"replicas": 2}}, "https://tpuf.example"), + ("WAIT", "mt_tenant_0000", 2, 30), + ("PATCH", "mt_tenant_0001", {"pinning": {"replicas": 2}}, "https://tpuf.example"), + ("WAIT", "mt_tenant_0001", 2, 30), + ] + + +def test_turbopuffer_supports_scalar_label_payload_for_multitenant_search() -> None: + from vectordb_bench.backend.clients.turbopuffer.turbopuffer import TurboPuffer + + fake_client = FakeTurboClient() + db = TurboPuffer.__new__(TurboPuffer) + db.client = fake_client + db.namespace = "single" + db.multitenant_namespace_prefix = "mt_" + db._ns_cache = {} + db._vector_field = "vector" + db._scalar_label_field = "label" + db._scalar_payload_label_field = "scalar_label" + db.expr = None + + assert db.supports_payload_profile(PayloadProfile.SCALAR_LABEL) + assert db.search_embedding( + [1.0, 0.0], + k=50, + payload_profile=PayloadProfile.SCALAR_LABEL, + tenant="tenant_0001", + ) == [10] + assert fake_client.namespaces["mt_tenant_0001"].query_calls[0]["include_attributes"] == ["scalar_label"] diff --git a/tests/test_oss_opensearch_fts.py b/tests/test_oss_opensearch_fts.py new file mode 100644 index 000000000..25b5b295c --- /dev/null +++ b/tests/test_oss_opensearch_fts.py @@ -0,0 +1,340 @@ +# ruff: noqa: E402 + +import sys +import types + +import pytest +from click.testing import CliRunner + + +class _FakeOpenSearch: + pass + + +sys.modules.setdefault("opensearchpy", types.SimpleNamespace(OpenSearch=_FakeOpenSearch)) + +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.api import IndexType +from vectordb_bench.backend.clients.elastic_cloud.config import ElasticCloudFtsConfig +from vectordb_bench.backend.clients.oss_opensearch import cli as oss_opensearch_cli +from vectordb_bench.backend.clients.oss_opensearch.config import OSSOpenSearchFtsConfig, OSSOpenSearchIndexConfig +from vectordb_bench.backend.clients.oss_opensearch.oss_opensearch import OSSOpenSearch, OpenSearchError +from vectordb_bench.backend.filter import NewIntFilter +from vectordb_bench.backend.payload import PayloadProfile +from vectordb_bench.cli.cli import select_cli_db_case_config + + +def make_fts_db(): + db = OSSOpenSearch.__new__(OSSOpenSearch) + db.index_name = "idx" + db.id_col_name = "doc_id" + db.text_col_name = "text" + db.filter_id_col_name = "filter_id" + db.filter = None + db._is_fts = True + db.client = object() + return db + + +def test_oss_opensearch_fts_config_defaults(): + config = OSSOpenSearchFtsConfig() + + assert config.index_param() == ElasticCloudFtsConfig().index_param() + assert config.index_param()["properties"]["doc_id"] == {"type": "keyword"} + assert config.index_param()["properties"]["filter_id"] == {"type": "long"} + assert config.index_param()["properties"]["text"] == {"type": "text"} + assert config.search_param() == {} + + +def test_oss_opensearch_fts_config_supports_bm25_similarity(): + config = OSSOpenSearchFtsConfig(bm25_k1=1.2, bm25_b=0.75) + elastic_config = ElasticCloudFtsConfig(bm25_k1=1.2, bm25_b=0.75) + + assert config.similarity_settings() == elastic_config.similarity_settings() + assert config.index_param()["properties"]["text"]["similarity"] == "vdbbench_bm25" + assert config.similarity_settings() == { + "similarity": { + "vdbbench_bm25": { + "type": "BM25", + "k1": 1.2, + "b": 0.75, + } + } + } + + +def test_oss_opensearch_declares_full_text_support(): + assert OSSOpenSearch.supports_full_text_search() is True + assert DB.OSSOpenSearch.case_config_cls(IndexType.FTS) is OSSOpenSearchFtsConfig + + +def test_oss_opensearch_fts_cli_does_not_require_vector_index_options(monkeypatch: pytest.MonkeyPatch): + captured = {} + monkeypatch.setattr(oss_opensearch_cli, "run", lambda **kwargs: captured.update(kwargs)) + + result = CliRunner().invoke( + oss_opensearch_cli.OSSOpenSearch, + ["--host", "localhost", "--case-type", "FTSBm25Performance", "--dry-run"], + ) + + assert result.exit_code == 0, result.output + assert isinstance(captured["db_case_config"], OSSOpenSearchFtsConfig) + + +def test_oss_opensearch_fts_cli_uses_single_node_defaults_and_force_merge_flag(monkeypatch: pytest.MonkeyPatch): + captured = {} + monkeypatch.setattr(oss_opensearch_cli, "run", lambda **kwargs: captured.update(kwargs)) + + result = CliRunner().invoke( + oss_opensearch_cli.OSSOpenSearch, + [ + "--host", + "localhost", + "--case-type", + "FTSBm25Performance", + "--force-merge-enabled", + "false", + "--dry-run", + ], + ) + + assert result.exit_code == 0, result.output + assert captured["db_case_config"].number_of_replicas == 0 + assert captured["db_case_config"].force_merge_enabled is False + + +def test_oss_opensearch_generic_fts_routing_preserves_force_merge(): + selected = select_cli_db_case_config( + DB.OSSOpenSearch, + OSSOpenSearchIndexConfig(force_merge_enabled=False), + "FTSBm25Performance", + ) + + assert isinstance(selected, OSSOpenSearchFtsConfig) + assert selected.force_merge_enabled is False + + +def test_oss_opensearch_green_health_wait_is_bounded_and_diagnostic(): + db = make_fts_db() + db.case_config = OSSOpenSearchFtsConfig(number_of_replicas=1) + calls = {} + + class Cluster: + def health(self, **kwargs): + calls.update(kwargs) + return { + "status": "yellow", + "timed_out": True, + "unassigned_shards": 1, + "number_of_nodes": 1, + } + + db.client = types.SimpleNamespace(cluster=Cluster()) + + with pytest.raises(OpenSearchError, match="configured_replicas=1.*unassigned_shards=1.*number_of_nodes=1"): + db._wait_till_green() + + assert calls == { + "index": "idx", + "wait_for_status": "green", + "timeout": "30m", + } + + +def test_oss_opensearch_create_index_fts_uses_text_mappings_and_settings(): + db = OSSOpenSearch.__new__(OSSOpenSearch) + db._is_fts = True + db.case_config = OSSOpenSearchFtsConfig( + number_of_shards=2, + number_of_replicas=1, + refresh_interval="10s", + ) + db.index_name = "idx" + calls = {} + + class Indices: + def create(self, **kwargs): + calls.update(kwargs) + + class Client: + indices = Indices() + + db._create_index(Client()) + + assert calls == { + "index": "idx", + "body": { + "settings": { + "index": { + "number_of_shards": 2, + "number_of_replicas": 1, + "refresh_interval": "10s", + } + }, + "mappings": { + "properties": { + "doc_id": {"type": "keyword"}, + "filter_id": {"type": "long"}, + "text": {"type": "text"}, + } + }, + }, + } + + +def test_oss_opensearch_insert_documents_builds_bulk_body(): + db = make_fts_db() + captured = {} + + class Client: + def bulk(self, **kwargs): + captured.update(kwargs) + return {"errors": False} + + db.client = Client() + + assert db.insert_documents(["alpha", "beta"], ["d1", "d2"], filter_ids=[0, 7]) == (2, None) + assert captured["body"] == [ + {"index": {"_index": "idx", "_id": "d1"}}, + {"doc_id": "d1", "text": "alpha", "filter_id": 0}, + {"index": {"_index": "idx", "_id": "d2"}}, + {"doc_id": "d2", "text": "beta", "filter_id": 7}, + ] + + +def test_oss_opensearch_insert_documents_reports_partial_bulk_failure(): + db = make_fts_db() + + class Client: + def bulk(self, **kwargs): + return { + "errors": True, + "items": [ + {"index": {"_id": "d1", "status": 201}}, + { + "index": { + "_id": "d2", + "status": 429, + "error": { + "type": "rejected_execution_exception", + "reason": "indexing queue is full", + }, + } + }, + ], + } + + db.client = Client() + + insert_count, error = db.insert_documents(["alpha", "beta"], ["d1", "d2"]) + + assert insert_count == 1 + assert isinstance(error, RuntimeError) + assert "failed for 1/2 documents" in str(error) + assert "successful=1" in str(error) + assert "id=d2" in str(error) + assert "rejected_execution_exception: indexing queue is full" in str(error) + + +def test_oss_opensearch_insert_documents_rejects_malformed_bulk_error_response(): + db = make_fts_db() + + class Client: + def bulk(self, **kwargs): + return {"errors": True} + + db.client = Client() + + insert_count, error = db.insert_documents(["alpha"], ["d1"]) + + assert insert_count == 0 + assert isinstance(error, RuntimeError) + assert str(error) == "OpenSearch FTS bulk response reported errors without an items list" + + +def test_oss_opensearch_insert_documents_validates_lengths(): + db = make_fts_db() + + class Client: + def bulk(self, **kwargs): + raise AssertionError("bulk should not be called") + + db.client = Client() + + with pytest.raises(ValueError, match=r"Mismatch between texts .* and doc_ids .* lengths"): + db.insert_documents(["alpha", "beta"], ["d1"]) + + +def test_oss_opensearch_search_documents_builds_match_query(): + db = make_fts_db() + calls = {} + + class Client: + def search(self, **kwargs): + calls.update(kwargs) + return {"hits": {"hits": [{"fields": {"doc_id": ["d1"]}}]}} + + db.client = Client() + + assert db.search_documents("hello world", k=3) == ["d1"] + assert calls["index"] == "idx" + assert calls["body"] == {"query": {"match": {"text": "hello world"}}} + assert calls["size"] == 3 + assert calls["stored_fields"] == "_none_" + assert calls["filter_path"] == ["hits.hits._id", "hits.hits.fields.doc_id"] + + +def test_oss_opensearch_search_documents_applies_fts_filter(): + db = make_fts_db() + calls = {} + + class Client: + def search(self, **kwargs): + calls.update(kwargs) + return {"hits": {"hits": [{"fields": {"doc_id": ["d1"]}}]}} + + db.client = Client() + db.prepare_filter(NewIntFilter(filter_rate=0.5, int_field="filter_id", int_value=10)) + + assert db.search_documents("hello world", k=3) == ["d1"] + assert calls["body"] == { + "query": { + "bool": { + "must": {"match": {"text": "hello world"}}, + "filter": {"range": {"filter_id": {"gte": 10}}}, + } + } + } + + +def test_oss_opensearch_search_documents_requests_text_payload(): + db = make_fts_db() + calls = {} + + class Client: + def search(self, **kwargs): + calls.update(kwargs) + return {"hits": {"hits": [{"_id": "d1", "_source": {"text": "hello"}}]}} + + db.client = Client() + + assert db.search_documents("hello world", k=3, payload_profile=PayloadProfile.TEXT) == ["d1"] + assert calls["_source"] == ["text"] + assert "stored_fields" not in calls + assert calls["filter_path"] == [ + "hits.hits._id", + "hits.hits.fields.doc_id", + "hits.hits._source.text", + ] + + +def test_oss_opensearch_document_methods_guard_non_fts_mode(): + db = OSSOpenSearch.__new__(OSSOpenSearch) + db._is_fts = False + db.client = object() + + with pytest.raises(RuntimeError, match="OSSOpenSearch full-text insert requires OSSOpenSearchFtsConfig"): + db.insert_documents(["alpha"], ["d1"]) + + with pytest.raises(RuntimeError, match="OSSOpenSearch full-text search requires OSSOpenSearchFtsConfig"): + db.search_documents("alpha") diff --git a/tests/test_pgvector.py b/tests/test_pgvector.py new file mode 100644 index 000000000..cdd9461a9 --- /dev/null +++ b/tests/test_pgvector.py @@ -0,0 +1,180 @@ +"""Tests for PgVector client and ConcurrentInsertRunner. + +Reproduces issue #756: insert fails with + TypeError: no default __reduce__ due to non-trivial __cinit__ +when ConcurrentInsertRunner deep-copies a PgVector instance that has a live +psycopg connection open (the connection is opened by `with self.db.init():` +inside task() before the deepcopy in _get_thread_db()). + +Requires: + docker run -d --name pgvector-test \ + -e POSTGRES_USER=vectordb -e POSTGRES_PASSWORD=vectordb \ + -e POSTGRES_DB=vectordb -p 5432:5432 \ + pgvector/pgvector:pg17 + +Usage: + pytest tests/test_pgvector.py -v -s +""" + +from __future__ import annotations + +import logging +import pickle +from unittest.mock import MagicMock + +import numpy as np +import pytest + +from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.clients.pgvector.config import PgVectorHNSWConfig +from vectordb_bench.backend.dataset import Dataset, DatasetSource +from vectordb_bench.backend.filter import Filter, FilterOp, non_filter +from vectordb_bench.backend.runner.concurrent_runner import ConcurrentInsertRunner + +log = logging.getLogger(__name__) + +# ── Connection config ──────────────────────────────────────────────────────── + +DB_CONFIG = { + "connect_config": { + "host": "localhost", + "port": 5432, + "dbname": "vectordb", + "user": "vectordb", + "password": "vectordb", + }, + "table_name": "test_pgvector", +} + +DIM = 128 +COUNT = 500 +RNG = np.random.default_rng(42) + + +# ── Helpers ────────────────────────────────────────────────────────────────── + + +def make_hnsw_config(**kwargs) -> PgVectorHNSWConfig: + return PgVectorHNSWConfig( + metric_type="COSINE", + m=16, + ef_construction=64, + ef_search=64, + **kwargs, + ) + + +def make_db(table_name: str = "test_pgvector", drop_old: bool = True) -> DB.PgVector.init_cls: + cfg = dict(DB_CONFIG) + cfg["table_name"] = table_name + return DB.PgVector.init_cls( + dim=DIM, + db_config=cfg, + db_case_config=make_hnsw_config(), + drop_old=drop_old, + ) + + +def random_embeddings(n: int = COUNT, d: int = DIM) -> list[list[float]]: + return RNG.random((n, d)).tolist() + + +# ── Basic client tests ──────────────────────────────────────────────────────── + + +class TestPgVectorBasic: + """Unit tests for the PgVector client (no subprocess).""" + + def test_insert_and_search(self): + db = make_db("test_basic") + embeddings = random_embeddings() + metadata = list(range(COUNT)) + + with db.init(): + count, err = db.insert_embeddings(embeddings=embeddings, metadata=metadata) + assert err is None, f"Insert error: {err}" + assert count == COUNT + + with db.init(): + db.optimize() + + with db.init(): + db.prepare_filter(Filter(type=FilterOp.NonFilter)) + results = db.search_embedding(query=embeddings[0], k=10) + assert len(results) > 0 + + def test_db_is_not_thread_safe(self): + db = make_db("test_thread_safe") + assert db.thread_safe is False + + def test_db_picklable_after_init(self): + """PgVector instance must be picklable after __init__ (conn/cursor are None). + + This is required for ConcurrentInsertRunner which spawns a subprocess + and pickles self (which includes self.db). + """ + db = make_db("test_pickle") + data = pickle.dumps(db) + db2 = pickle.loads(data) # noqa: S301 + assert db2.dim == DIM + + def test_get_thread_db_with_open_connection(self): + """Regression test for issue #756. + + ConcurrentInsertRunner.task() opens `with self.db.init()` before calling + workers. For non-thread-safe DBs the original _get_thread_db() then called + deepcopy(self.db) — but the live psycopg C-extension Connection is not + deep-copyable, causing TypeError. + + Fixed code returns self.db directly (no deepcopy), so this test must pass + without raising. + """ + db = make_db("test_get_thread_db") + runner = ConcurrentInsertRunner(db=db, dataset=MagicMock(), normalize=False) + + with db.init(): + assert db.conn is not None + result = runner._get_thread_db() # TypeError here on original code + + assert result is db + + +# ── ConcurrentInsertRunner tests ────────────────────────────────────────────── + + +class TestPgVectorConcurrentInsert: + """Tests for ConcurrentInsertRunner with PgVector (reproduces issue #756).""" + + @pytest.mark.integration + def test_concurrent_insert_e2e(self): + """E2E regression test for issue #756 using the OpenAI 50K dataset. + + Exercises the full pipeline: + ProcessPoolExecutor(spawn) → pickle runner → subprocess task() + → with self.db.init() → worker _get_thread_db() → insert batches + + FAILS on original code (TypeError: deepcopy of live psycopg connection). + PASSES on fixed code. + """ + dataset = Dataset.OPENAI.manager(50_000) + dataset.prepare(DatasetSource.AliyunOSS) + + cfg = dict(DB_CONFIG) + cfg["table_name"] = "test_e2e_insert" + db = DB.PgVector.init_cls( + dim=dataset.data.dim, + db_config=cfg, + db_case_config=PgVectorHNSWConfig( + metric_type="COSINE", + m=16, + ef_construction=64, + ef_search=64, + ), + drop_old=True, + ) + + runner = ConcurrentInsertRunner(db=db, dataset=dataset, normalize=True, filters=non_filter) + count = runner.run() + + assert count == 50_000, f"Expected 50000 rows, got {count}" + log.info(f"E2E insert completed: {count} rows") diff --git a/tests/test_pinecone_multitenant.py b/tests/test_pinecone_multitenant.py new file mode 100644 index 000000000..1a81d55ff --- /dev/null +++ b/tests/test_pinecone_multitenant.py @@ -0,0 +1,214 @@ +import threading +from types import SimpleNamespace + + +class FakePineconeIndex: + def __init__(self): + self.upserts = [] + self.queries = [] + self.deletes = [] + + def describe_index_stats(self): + return {"dimension": 2, "total_vector_count": 3, "namespaces": {"mt_tenant_0000": {}, "mt_tenant_0001": {}}} + + def upsert(self, vectors, namespace=None): + self.upserts.append((vectors, namespace)) + return SimpleNamespace(_response_info={"raw_headers": {"x-pinecone-request-lsn": "7"}}) + + def query(self, **kwargs): + self.queries.append(kwargs) + return SimpleNamespace( + matches=[{"id": "11"}], + _response_info={"raw_headers": {"x-pinecone-max-indexed-lsn": "7"}}, + ) + + def delete(self, delete_all=False, namespace=None): + self.deletes.append((delete_all, namespace)) + + +class FailingPineconeIndex(FakePineconeIndex): + def upsert(self, vectors, namespace=None): + self.upserts.append((vectors, namespace)) + if namespace == "mt_tenant_0001": + raise RuntimeError("tenant upsert failed") + return SimpleNamespace(_response_info={"raw_headers": {"x-pinecone-request-lsn": "7"}}) + + +class FakePineconeClient: + def __init__(self, index): + self.index = index + + def Index(self, name): + return self.index + + +def test_pinecone_groups_multitenant_upsert_and_query(monkeypatch): + from vectordb_bench.backend.clients.pinecone import pinecone as pinecone_module + from vectordb_bench.backend.clients.pinecone.pinecone import Pinecone + + fake_index = FakePineconeIndex() + monkeypatch.setattr( + pinecone_module.pinecone, + "Pinecone", + lambda api_key: FakePineconeClient(fake_index), + ) + + db = Pinecone( + dim=2, + db_config={ + "api_key": "k", + "index_name": "idx", + "multitenant_namespace_prefix": "mt_", + }, + db_case_config=None, + drop_old=False, + ) + db.set_multitenant_context(["tenant_0000", "tenant_0001"]) + + with db.init(): + count, err = db.insert_embeddings( + embeddings=[[1.0, 0.0], [0.0, 1.0], [0.5, 0.5]], + metadata=[0, 1, 2], + tenant_labels_data=["tenant_0000", "tenant_0001", "tenant_0000"], + ) + result = db.search_embedding([1.0, 0.0], k=1, tenant="tenant_0001") + + assert count == 3 + assert err is None + assert result == [11] + assert fake_index.upserts[0][1] == "mt_tenant_0000" + assert fake_index.upserts[1][1] == "mt_tenant_0001" + assert fake_index.queries[-1]["namespace"] == "mt_tenant_0001" + + +def test_pinecone_multitenant_upsert_preserves_scalar_payload_labels(monkeypatch): + from vectordb_bench.backend.clients.pinecone import pinecone as pinecone_module + from vectordb_bench.backend.clients.pinecone.pinecone import Pinecone + + fake_index = FakePineconeIndex() + monkeypatch.setattr( + pinecone_module.pinecone, + "Pinecone", + lambda api_key: FakePineconeClient(fake_index), + ) + + db = Pinecone( + dim=2, + db_config={ + "api_key": "k", + "index_name": "idx", + "multitenant_namespace_prefix": "mt_", + }, + db_case_config=None, + drop_old=False, + with_scalar_labels=True, + ) + + with db.init(): + count, err = db.insert_embeddings( + embeddings=[[1.0, 0.0], [0.0, 1.0]], + metadata=[0, 1], + labels_data=["label_a", "label_b"], + tenant_labels_data=["tenant_0000", "tenant_0001"], + ) + + assert count == 2 + assert err is None + assert fake_index.upserts[0][0][0][2]["label"] == "label_a" + assert fake_index.upserts[1][0][0][2]["label"] == "label_b" + + +def test_pinecone_multitenant_partial_insert_failure_is_explicit(monkeypatch): + from vectordb_bench.backend.clients.pinecone import pinecone as pinecone_module + from vectordb_bench.backend.clients.pinecone.pinecone import Pinecone + + fake_index = FailingPineconeIndex() + monkeypatch.setattr( + pinecone_module.pinecone, + "Pinecone", + lambda api_key: FakePineconeClient(fake_index), + ) + + db = Pinecone( + dim=2, + db_config={ + "api_key": "k", + "index_name": "idx", + "multitenant_namespace_prefix": "mt_", + }, + db_case_config=None, + drop_old=False, + ) + + with db.init(): + count, err = db.insert_embeddings( + embeddings=[[1.0, 0.0], [0.0, 1.0], [0.5, 0.5]], + metadata=[0, 1, 2], + tenant_labels_data=["tenant_0000", "tenant_0001", "tenant_0000"], + ) + + assert count == 2 + assert getattr(err, "non_retryable", False) is True + assert getattr(err, "inserted_count") == 2 + assert getattr(err, "successful_tenants") == {"tenant_0000": 2} + assert getattr(err, "failed_tenant") == "tenant_0001" + assert getattr(err, "failed_tenant_count") == 1 + assert db._multitenant_insert_counts == {"tenant_0000": 2} + + +def test_pinecone_multitenant_insert_counts_are_thread_safe(monkeypatch): + from vectordb_bench.backend.clients.pinecone import pinecone as pinecone_module + from vectordb_bench.backend.clients.pinecone.pinecone import Pinecone + + class RacingCounts(dict): + def __init__(self, lock_getter): + super().__init__() + self.barrier = threading.Barrier(2) + self.lock_getter = lock_getter + + def get(self, key, default=None): + value = super().get(key, default) + lock = self.lock_getter() + if key == "tenant_0000" and (lock is None or not lock.locked()): + self.barrier.wait(timeout=5) + return value + + fake_index = FakePineconeIndex() + monkeypatch.setattr( + pinecone_module.pinecone, + "Pinecone", + lambda api_key: FakePineconeClient(fake_index), + ) + + db = Pinecone( + dim=2, + db_config={ + "api_key": "k", + "index_name": "idx", + "multitenant_namespace_prefix": "mt_", + }, + db_case_config=None, + drop_old=False, + ) + db._multitenant_insert_counts = RacingCounts(lambda: getattr(db, "_multitenant_insert_counts_lock", None)) + results = [] + + def insert_one(row_id): + results.append( + db.insert_embeddings( + embeddings=[[float(row_id), 0.0]], + metadata=[row_id], + tenant_labels_data=["tenant_0000"], + ) + ) + + with db.init(): + threads = [threading.Thread(target=insert_one, args=(row_id,)) for row_id in (1, 2)] + for thread in threads: + thread.start() + for thread in threads: + thread.join() + + assert len(fake_index.upserts) == 2 + assert sorted(results) == [(1, None), (1, None)] + assert dict(db._multitenant_insert_counts) == {"tenant_0000": 2} diff --git a/tests/test_turbopuffer_cli.py b/tests/test_turbopuffer_cli.py new file mode 100644 index 000000000..46db473ca --- /dev/null +++ b/tests/test_turbopuffer_cli.py @@ -0,0 +1,338 @@ +from types import SimpleNamespace + +from click.testing import CliRunner +from pytest import MonkeyPatch + +from vectordb_bench.backend.clients.api import MetricType +from vectordb_bench.backend.clients.turbopuffer import cli as turbopuffer_cli +from vectordb_bench.backend.clients.turbopuffer import turbopuffer as turbopuffer_client +from vectordb_bench.backend.clients.turbopuffer.turbopuffer import TurboPuffer + + +def test_turbopuffer_cli_accepts_multitenant_namespace_prefix_and_metric_type( + monkeypatch: MonkeyPatch, +) -> None: + captured = {} + + def fake_run(**kwargs): + captured.update(kwargs) + + monkeypatch.setattr(turbopuffer_cli, "run", fake_run) + + result = CliRunner().invoke( + turbopuffer_cli.TurboPuffer, + [ + "--skip-drop-old", + "--skip-load", + "--skip-search-serial", + "--search-concurrent", + "--case-type", + "CloudMultiTenantSearchCase", + "--api-key", + "secret", + "--region", + "aws-us-west-2", + "--namespace", + "cohere10m_multitenant", + "--multitenant-namespace-prefix", + "cohere10m_", + "--scalar-payload-label-field", + "scalar_label", + "--metric-type", + "COSINE", + "--dry-run", + ], + ) + + assert result.exit_code == 0 + assert captured["db_config"].multitenant_namespace_prefix == "cohere10m_" + assert captured["db_config"].scalar_payload_label_field == "scalar_label" + assert captured["db_case_config"].metric_type == MetricType.COSINE + assert captured["db_case_config"].multitenant_warmup_policy == "none" + + +def test_turbopuffer_cli_skips_pin_namespace_during_dry_run(monkeypatch: MonkeyPatch) -> None: + calls = [] + captured = {} + + def fake_metadata_request(api_key, region, namespace, method, payload=None, api_base_url=None): + calls.append((method, payload, api_key, region, namespace, api_base_url)) + return {} + + def fake_wait_for_pinning(api_key, region, namespace, replicas, api_base_url=None, timeout=None): + calls.append(("WAIT", replicas, api_key, region, namespace, api_base_url, timeout)) + return {"pinning": {"replicas": replicas, "status": {"ready_replicas": replicas}}} + + def fake_run(**kwargs): + captured.update(kwargs) + + monkeypatch.setattr(turbopuffer_client, "namespace_metadata_request", fake_metadata_request) + monkeypatch.setattr(turbopuffer_client, "wait_for_namespace_pinning", fake_wait_for_pinning) + monkeypatch.setattr(turbopuffer_cli, "run", fake_run) + + result = CliRunner().invoke( + turbopuffer_cli.TurboPuffer, + [ + "--skip-drop-old", + "--skip-load", + "--skip-search-serial", + "--search-concurrent", + "--case-type", + "CloudPayloadSearchCase", + "--api-key", + "secret", + "--region", + "aws-us-west-2", + "--namespace", + "laion100m", + "--pin-namespace", + "--pin-replicas", + "2", + "--pin-timeout", + "7200", + "--metric-type", + "COSINE", + "--disable-backpressure", + "--dry-run", + ], + ) + + assert result.exit_code == 0, result.output + assert calls == [] + assert captured["db_config"].pin_namespace is False + assert captured["db_config"].pin_namespace_requested is True + assert captured["db_config"].pin_replicas == 2 + assert captured["db_config"].pin_timeout == 7200 + assert captured["db_config"].pin_target_namespace_count == 1 + assert captured["db_case_config"].metric_type == MetricType.COSINE + assert captured["db_case_config"].disable_backpressure is True + + +def test_turbopuffer_cli_accepts_multitenant_warmup_policy( + monkeypatch: MonkeyPatch, +) -> None: + captured = {} + + def fake_run(**kwargs): + captured.update(kwargs) + + monkeypatch.setattr(turbopuffer_cli, "run", fake_run) + + result = CliRunner().invoke( + turbopuffer_cli.TurboPuffer, + [ + "--case-type", + "CloudMultiTenantSearchCase", + "--api-key", + "secret", + "--region", + "aws-us-west-2", + "--multitenant-warmup-policy", + "all", + "--dry-run", + ], + ) + + assert result.exit_code == 0, result.output + assert captured["db_case_config"].multitenant_warmup_policy == "all" + + +def test_turbopuffer_multitenant_optimize_skips_base_namespace_by_default( + monkeypatch: MonkeyPatch, +) -> None: + warmed = [] + + class FakeNamespace: + def __init__(self, name: str): + self.name = name + + def hint_cache_warm(self): + warmed.append(self.name) + + class FakeClient: + def namespace(self, name: str): + return FakeNamespace(name) + + monkeypatch.setattr(turbopuffer_client.time, "sleep", lambda _seconds: None) + db = object.__new__(TurboPuffer) + db.client = FakeClient() + db.ns = FakeNamespace("base") + db.namespace = "base" + db.multitenant_namespace_prefix = "mt_" + db.multitenant_tenant_labels = ["tenant_0000", "tenant_0001"] + db._ns_cache = {} + db.db_case_config = SimpleNamespace(time_wait_warmup=1, multitenant_warmup_policy="none") + + db.optimize() + + assert warmed == [] + + +def test_turbopuffer_multitenant_optimize_can_warm_all_tenant_namespaces( + monkeypatch: MonkeyPatch, +) -> None: + warmed = [] + + class FakeNamespace: + def __init__(self, name: str): + self.name = name + + def hint_cache_warm(self): + warmed.append(self.name) + + class FakeClient: + def namespace(self, name: str): + return FakeNamespace(name) + + monkeypatch.setattr(turbopuffer_client.time, "sleep", lambda _seconds: None) + db = object.__new__(TurboPuffer) + db.client = FakeClient() + db.ns = FakeNamespace("base") + db.namespace = "base" + db.multitenant_namespace_prefix = "mt_" + db.multitenant_tenant_labels = ["tenant_0000", "tenant_0001"] + db._ns_cache = {} + db.db_case_config = SimpleNamespace(time_wait_warmup=1, multitenant_warmup_policy="all") + + db.optimize() + + assert warmed == ["mt_tenant_0000", "mt_tenant_0001"] + + +def test_turbopuffer_cli_skips_multitenant_pin_namespaces_during_dry_run( + monkeypatch: MonkeyPatch, +) -> None: + calls = [] + captured = {} + + def fake_metadata_request(api_key, region, namespace, method, payload=None, api_base_url=None): + calls.append((method, namespace, payload)) + return {} + + def fake_wait_for_pinning(api_key, region, namespace, replicas, api_base_url=None, timeout=None): + calls.append(("WAIT", namespace, replicas)) + return {"pinning": {"replicas": replicas, "status": {"ready_replicas": replicas}}} + + def fake_run(**kwargs): + captured.update(kwargs) + + monkeypatch.setattr(turbopuffer_client, "namespace_metadata_request", fake_metadata_request) + monkeypatch.setattr(turbopuffer_client, "wait_for_namespace_pinning", fake_wait_for_pinning) + monkeypatch.setattr(turbopuffer_cli, "run", fake_run) + + result = CliRunner().invoke( + turbopuffer_cli.TurboPuffer, + [ + "--skip-drop-old", + "--skip-load", + "--skip-search-serial", + "--search-concurrent", + "--case-type", + "CloudMultiTenantSearchCase", + "--tenant-count", + "2", + "--tenant-prefix", + "tenant_", + "--tenant-id-width", + "4", + "--api-key", + "secret", + "--region", + "aws-us-west-2", + "--namespace", + "unused_single_namespace", + "--multitenant-namespace-prefix", + "cohere10m_", + "--pin-namespace", + "--pin-replicas", + "1", + "--dry-run", + ], + ) + + assert result.exit_code == 0, result.output + assert calls == [] + assert captured["db_config"].pin_namespace is False + assert captured["db_config"].pin_namespace_requested is True + assert captured["db_config"].pin_target_namespace_count == 2 + assert captured["db_config"].multitenant_namespace_prefix == "cohere10m_" + + +def test_turbopuffer_unpin_namespace_uses_pin_timeout(monkeypatch: MonkeyPatch) -> None: + calls = [] + + def fake_metadata_request(api_key, region, namespace, method, payload=None, api_base_url=None): + calls.append((method, payload, api_key, region, namespace, api_base_url)) + return {} + + def fake_wait_for_pinning(api_key, region, namespace, replicas, api_base_url=None, timeout=None): + calls.append(("WAIT", replicas, api_key, region, namespace, api_base_url, timeout)) + return {"pinning": None} + + monkeypatch.setattr(turbopuffer_client, "namespace_metadata_request", fake_metadata_request) + monkeypatch.setattr(turbopuffer_client, "wait_for_namespace_pinning", fake_wait_for_pinning) + + result = CliRunner().invoke( + turbopuffer_cli.TurboPufferUnpin, + [ + "--api-key", + "secret", + "--region", + "aws-us-west-2", + "--namespace", + "laion100m", + "--pin-timeout", + "7200", + ], + ) + + assert result.exit_code == 0, result.output + assert calls == [ + ("PATCH", {"pinning": None}, "secret", "aws-us-west-2", "laion100m", None), + ("WAIT", None, "secret", "aws-us-west-2", "laion100m", None, 7200), + ] + + +def test_turbopuffer_fts_insert_declares_filter_id_filterable() -> None: + writes = [] + + class FakeNamespace: + def write(self, **kwargs): + writes.append(kwargs) + + db = object.__new__(TurboPuffer) + db.ns = FakeNamespace() + db._is_fts = True + db._text_field = "text" + db._scalar_id_field = "id" + db._filter_id_field = "filter_id" + db.db_case_config = SimpleNamespace(disable_backpressure=False) + + count, error = db.insert_documents( + texts=["alpha", "beta"], + doc_ids=["doc-1", "doc-2"], + filter_ids=[10, 20], + ) + + assert error is None + assert count == 2 + assert writes == [ + { + "upsert_columns": { + "id": ["doc-1", "doc-2"], + "text": ["alpha", "beta"], + "filter_id": [10, 20], + }, + "schema": { + "text": { + "type": "string", + "full_text_search": True, + }, + "filter_id": { + "type": "int", + "filterable": True, + }, + }, + "disable_backpressure": False, + } + ] diff --git a/tests/test_volc_mysql_client.py b/tests/test_volc_mysql_client.py new file mode 100644 index 000000000..ab602be9d --- /dev/null +++ b/tests/test_volc_mysql_client.py @@ -0,0 +1,236 @@ +from vectordb_bench.backend.clients.volc_mysql.volc_mysql import VolcMySQL + + +def test_volc_mysql_is_not_thread_safe(): + # mysql.connector is not thread-safe; ConcurrentInsertRunner relies on + # this class attribute to clamp max_workers=1. + assert VolcMySQL.thread_safe is False + + +def test_volc_mysql_ready_to_load_does_not_return_falsy_none(): + # The base VectorDB has no ready_to_load() method to override here; + # the previous stub returned None, which any `if not db.ready_to_load()` + # caller would misread as "not ready". + assert not hasattr(VolcMySQL, "ready_to_load") or VolcMySQL.ready_to_load.__qualname__.startswith("VectorDB") + + +from vectordb_bench.backend.filter import FilterOp + + +def test_volc_mysql_supports_numge_filter(): + # Matches the WHERE id >= %s prepared SQL the client already builds in init(). + assert FilterOp.NumGE in VolcMySQL.supported_filter_types + assert FilterOp.NonFilter in VolcMySQL.supported_filter_types + + +from pydantic import SecretStr + +from vectordb_bench.backend.clients.volc_mysql.config import VolcMySQLConfig + + +def test_volc_mysql_config_to_dict_has_only_driver_kwargs(): + cfg = VolcMySQLConfig(password=SecretStr("pw")) + d = cfg.to_dict() + # mysql.connector.connect accepts these; the previously-declared + # read_timeout/write_timeout are not valid kwargs for that driver. + assert set(d.keys()) == {"host", "port", "user", "password"} + assert d["user"] == "root" + assert d["password"] == "pw" + + +from vectordb_bench.backend.clients.volc_mysql.config import VolcMySQLHNSWConfig + + +def test_volc_mysql_hnsw_config_optional_fields_default_to_none(): + cfg = VolcMySQLHNSWConfig() + assert cfg.M is None + assert cfg.ef_search is None + assert cfg.ef_construction is None + assert cfg.quant_algorithm is None + assert cfg.quant_type is None + + +from vectordb_bench.backend.clients.volc_mysql.volc_mysql import _build_index_attrs_json + + +def test_build_index_attrs_json_drops_none_values(): + j = _build_index_attrs_json({ + "metric_type": "l2", + "M": 16, + "ef_construction": 128, + "quant_algorithm": None, + "quant_type": None, + }) + # Compact JSON, deterministic key order (insertion order from helper). + assert j == '{"algorithm":"hnsw","distance":"l2","m":16,"ef_construction":128}' + + +def test_build_index_attrs_json_includes_quantization_when_set(): + j = _build_index_attrs_json({ + "metric_type": "cosine", + "M": 32, + "ef_construction": 200, + "quant_algorithm": "SQ", + "quant_type": "8_bit", + }) + assert j == ( + '{"algorithm":"hnsw","distance":"cosine","m":32,"ef_construction":200,' + '"quant_algorithm":"SQ","quant_type":"8_bit"}' + ) + + +from unittest.mock import MagicMock + +from pydantic import SecretStr as _SecretStr + +from vectordb_bench.backend.filter import IntFilter, NonFilter + + +def _make_client_with_mock_cursor(): + """Build a VolcMySQL without touching a DB (drop_old=False does not connect), + then wire up the prebuilt SQL templates and a mock cursor so search_embedding + can be exercised offline. + """ + cfg = VolcMySQLConfig(password=_SecretStr("pw")).to_dict() + client = VolcMySQL(dim=4, db_config=cfg, db_case_config=VolcMySQLHNSWConfig(), drop_old=False) + client.select_sql = "SELECT id FROM t ORDER BY d LIMIT %s" + client.select_sql_with_filter = "SELECT id FROM t WHERE id >= %s ORDER BY d LIMIT %s" + client._binary_vec = False + cursor = MagicMock() + cursor.fetchall.return_value = [(1,), (2,)] + client.conn = MagicMock() + client.cursor = cursor + return client, cursor + + +def test_search_embedding_uses_unfiltered_sql_by_default(): + client, cursor = _make_client_with_mock_cursor() + client.search_embedding([0.1, 0.2, 0.3, 0.4], k=10) + sql, params = cursor.execute.call_args[0] + assert sql == client.select_sql + assert params == ("[0.1, 0.2, 0.3, 0.4]", 10) + + +def test_search_embedding_applies_numge_filter_after_prepare_filter(): + # Regression guard: runners apply filters via prepare_filter(), never by + # passing filters to search_embedding. Without prepare_filter wiring the + # client silently ran an unfiltered search and reported wrong recall. + client, cursor = _make_client_with_mock_cursor() + client.prepare_filter(IntFilter(int_value=500, filter_rate=0.99)) + client.search_embedding([0.1, 0.2, 0.3, 0.4], k=10) + sql, params = cursor.execute.call_args[0] + assert sql == client.select_sql_with_filter + assert params == (500, "[0.1, 0.2, 0.3, 0.4]", 10) + + +def test_prepare_filter_nonfilter_resets_to_unfiltered(): + client, cursor = _make_client_with_mock_cursor() + client.prepare_filter(IntFilter(int_value=500, filter_rate=0.99)) + client.prepare_filter(NonFilter()) + client.search_embedding([0.1, 0.2, 0.3, 0.4], k=10) + sql, _ = cursor.execute.call_args[0] + assert sql == client.select_sql + + +import tempfile + +import pytest + +from vectordb_bench.backend.clients.volc_mysql import volc_mysql as volc_mysql_module + + +def _make_uninitialized_client(): + cfg = VolcMySQLConfig(password=_SecretStr("pw")).to_dict() + return VolcMySQL(dim=4, db_config=cfg, db_case_config=VolcMySQLHNSWConfig(), drop_old=False) + + +def test_init_closes_connection_when_create_database_fails(monkeypatch): + """If admin_cursor.execute(CREATE DATABASE ...) raises inside init(), + the open connection and both cursors must still be closed and the + instance attributes reset to None. Prior to the fix this leaked because + setup ran outside the try/finally. + """ + client = _make_uninitialized_client() + conn = MagicMock(name="conn") + cursor = MagicMock(name="cursor") + admin_cursor = MagicMock(name="admin_cursor") + admin_cursor.execute.side_effect = RuntimeError("CREATE DATABASE failed") + monkeypatch.setattr(client, "_create_connection", lambda: (conn, cursor, admin_cursor)) + + with pytest.raises(RuntimeError, match="CREATE DATABASE failed"): + with client.init(): + pass + + cursor.close.assert_called_once() + admin_cursor.close.assert_called_once() + conn.close.assert_called_once() + assert client.conn is None + assert client.cursor is None + assert client.admin_cursor is None + + +def test_select_sql_does_not_hardcode_force_index(monkeypatch): + """Streaming/read_write cases search before optimize() creates idx_v, + so FORCE INDEX(idx_v) in the prebuilt SQL would fail every pre-optimize + search with "Key 'idx_v' doesn't exist in table". + """ + from vectordb_bench.backend.clients.api import MetricType + + cfg = VolcMySQLConfig(password=_SecretStr("pw")).to_dict() + case_cfg = VolcMySQLHNSWConfig(metric_type=MetricType.COSINE) + client = VolcMySQL(dim=4, db_config=cfg, db_case_config=case_cfg, drop_old=False) + conn = MagicMock() + cursor = MagicMock() + admin_cursor = MagicMock() + monkeypatch.setattr(client, "_create_connection", lambda: (conn, cursor, admin_cursor)) + monkeypatch.setattr(client, "_probe_binary_support", lambda: False) + + with client.init(): + assert "FORCE INDEX" not in client.select_sql + assert "FORCE INDEX" not in client.select_sql_with_filter + assert "idx_v" not in client.select_sql + assert "idx_v" not in client.select_sql_with_filter + + +def test_insert_embeddings_returns_actual_rowcount_on_partial_load(tmp_path, monkeypatch): + """LOAD DATA is already committed when the rowcount mismatch is detected; + reporting (0, err) corrupts the serial runner's already_insert_count and + sends its retry into a PK-collision wall. Return (actual, err) so the + runner slices past the already-loaded prefix on retry. + """ + client = _make_uninitialized_client() + client._batch_counter = 0 + client._binary_vec = False + cursor = MagicMock() + cursor.rowcount = 7 + client.cursor = cursor + client.conn = MagicMock() + monkeypatch.setattr(volc_mysql_module.tempfile, "gettempdir", lambda: str(tmp_path)) + + embeddings = [[float(i)] * 4 for i in range(10)] + metadata = list(range(10)) + actual, err = client.insert_embeddings(embeddings, metadata) + + assert actual == 7 + assert isinstance(err, RuntimeError) + assert "wrote 7 rows, expected 10" in str(err) + + +def test_insert_embeddings_returns_n_on_full_load(tmp_path, monkeypatch): + """Sanity: when rowcount matches, return (n, None). Guards against the + partial-load fix regressing the happy path. + """ + client = _make_uninitialized_client() + client._batch_counter = 0 + client._binary_vec = False + cursor = MagicMock() + cursor.rowcount = 10 + client.cursor = cursor + client.conn = MagicMock() + monkeypatch.setattr(volc_mysql_module.tempfile, "gettempdir", lambda: str(tmp_path)) + + embeddings = [[float(i)] * 4 for i in range(10)] + metadata = list(range(10)) + actual, err = client.insert_embeddings(embeddings, metadata) + assert actual == 10 + assert err is None diff --git a/tests/test_volc_mysql_encoder.py b/tests/test_volc_mysql_encoder.py new file mode 100644 index 000000000..fe656bf29 --- /dev/null +++ b/tests/test_volc_mysql_encoder.py @@ -0,0 +1,74 @@ +import struct + +from vectordb_bench.backend.clients.volc_mysql.volc_mysql import _encode_batch_to_tsv + + +def test_encode_batch_to_tsv_sorts_by_id_and_hex_encodes(tmp_path): + tsv = tmp_path / "batch.tsv" + _encode_batch_to_tsv( + metadata=[42, 7, 99], + embeddings=[[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]], + dim=2, + tsv_path=str(tsv), + ) + + lines = tsv.read_text().splitlines() + assert len(lines) == 3 + + # Sorted by id ascending: 7, 42, 99 + assert lines[0].startswith("7\t") + assert lines[1].startswith("42\t") + assert lines[2].startswith("99\t") + + # The (3.0, 4.0) embedding was paired with id=7 + _, hex_str = lines[0].split("\t") + floats = struct.unpack("<2f", bytes.fromhex(hex_str)) + assert floats == (3.0, 4.0) + + +def test_encode_batch_to_tsv_writes_dim_floats_per_row(tmp_path): + tsv = tmp_path / "batch.tsv" + _encode_batch_to_tsv( + metadata=[1], + embeddings=[[0.5] * 1536], + dim=1536, + tsv_path=str(tsv), + ) + + line = tsv.read_text().rstrip("\n") + id_str, hex_str = line.split("\t") + assert id_str == "1" + # 1536 floats * 4 bytes * 2 hex chars = 12288 chars + assert len(hex_str) == 12288 + assert bytes.fromhex(hex_str) == struct.pack("<1536f", *([0.5] * 1536)) + + +def test_encode_batch_to_tsv_empty_batch(tmp_path): + tsv = tmp_path / "batch.tsv" + _encode_batch_to_tsv(metadata=[], embeddings=[], dim=4, tsv_path=str(tsv)) + assert tsv.read_text() == "" + + +def test_encode_batch_to_tsv_text_mode_sorts_and_formats(tmp_path): + # binary=False (to_vector fallback): rows sorted by id, vector written as a + # delimiter-safe "[f1,f2,...]" literal with no tab/newline inside the field. + tsv = tmp_path / "batch.tsv" + _encode_batch_to_tsv( + metadata=[42, 7, 99], + embeddings=[[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]], + dim=2, + tsv_path=str(tsv), + binary=False, + ) + + lines = tsv.read_text().splitlines() + assert len(lines) == 3 + # Sorted by id ascending: 7, 42, 99; id=7 was paired with (3.0, 4.0) + assert lines[0] == "7\t[3.0,4.0]" + assert lines[1] == "42\t[1.0,2.0]" + assert lines[2] == "99\t[5.0,6.0]" + # field is delimiter-safe: no tab/newline inside the bracketed literal + for line in lines: + _id, vec = line.split("\t") + assert vec.startswith("[") and vec.endswith("]") + assert "\t" not in vec diff --git a/uv.lock b/uv.lock deleted file mode 100644 index f21ca348c..000000000 --- a/uv.lock +++ /dev/null @@ -1,5607 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.11" -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version == '3.12.*'", - "python_full_version < '3.12'", -] - -[[package]] -name = "aiobotocore" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "aioitertools" }, - { name = "botocore" }, - { name = "jmespath" }, - { name = "multidict" }, - { name = "python-dateutil" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b8/50/a48ed11b15f926ce3dbb33e7fb0f25af17dbb99bcb7ae3b30c763723eca7/aiobotocore-3.4.0.tar.gz", hash = "sha256:a918b5cb903f81feba7e26835aed4b5e6bb2d0149d7f42bb2dd7d8089e3d9000", size = 122360, upload-time = "2026-04-07T06:12:24.884Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/d8/ce9386e6d76ea79e61dee15e62aa48cff6be69e89246b0ac4a11857cb02c/aiobotocore-3.4.0-py3-none-any.whl", hash = "sha256:26290eb6830ea92d8a6f5f90b56e9f5cedd6d126074d5db63b195e281d982465", size = 88018, upload-time = "2026-04-07T06:12:22.684Z" }, -] - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, -] - -[[package]] -name = "aiohttp" -version = "3.13.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513, upload-time = "2026-03-31T21:57:02.146Z" }, - { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748, upload-time = "2026-03-31T21:57:04.275Z" }, - { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673, upload-time = "2026-03-31T21:57:06.208Z" }, - { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757, upload-time = "2026-03-31T21:57:07.882Z" }, - { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152, upload-time = "2026-03-31T21:57:09.946Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010, upload-time = "2026-03-31T21:57:12.157Z" }, - { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251, upload-time = "2026-03-31T21:57:14.023Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969, upload-time = "2026-03-31T21:57:16.146Z" }, - { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871, upload-time = "2026-03-31T21:57:17.856Z" }, - { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844, upload-time = "2026-03-31T21:57:19.679Z" }, - { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969, upload-time = "2026-03-31T21:57:22.006Z" }, - { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193, upload-time = "2026-03-31T21:57:24.256Z" }, - { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477, upload-time = "2026-03-31T21:57:26.364Z" }, - { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198, upload-time = "2026-03-31T21:57:28.316Z" }, - { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321, upload-time = "2026-03-31T21:57:30.549Z" }, - { url = "https://files.pythonhosted.org/packages/88/06/e4a2e49255ea23fa4feeb5ab092d90240d927c15e47b5b5c48dff5a9ce29/aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06", size = 439069, upload-time = "2026-03-31T21:57:32.388Z" }, - { url = "https://files.pythonhosted.org/packages/c0/43/8c7163a596dab4f8be12c190cf467a1e07e4734cf90eebb39f7f5d53fc6a/aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8", size = 462859, upload-time = "2026-03-31T21:57:34.455Z" }, - { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, - { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, - { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, - { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, - { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, - { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, - { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, - { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, - { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, - { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, - { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, - { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, - { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, - { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, - { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" }, - { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, - { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, - { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, - { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, - { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, - { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, - { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, - { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, - { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, - { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, - { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, - { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, - { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, - { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, - { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, - { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, - { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, - { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, - { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, - { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, - { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, - { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, - { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, - { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, - { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, - { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, - { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, - { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, - { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, - { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, - { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, - { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, - { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, - { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, - { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, - { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, - { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, - { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, - { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, - { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, - { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, - { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, -] - -[[package]] -name = "aioitertools" -version = "0.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/3c/53c4a17a05fb9ea2313ee1777ff53f5e001aefd5cc85aa2f4c2d982e1e38/aioitertools-0.13.0.tar.gz", hash = "sha256:620bd241acc0bbb9ec819f1ab215866871b4bbd1f73836a55f799200ee86950c", size = 19322, upload-time = "2025-11-06T22:17:07.609Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl", hash = "sha256:0be0292b856f08dfac90e31f4739432f4cb6d7520ab9eb73e143f4f2fa5259be", size = 24182, upload-time = "2025-11-06T22:17:06.502Z" }, -] - -[[package]] -name = "aiosignal" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, -] - -[[package]] -name = "alibabacloud-darabonba-encode-util" -version = "0.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/d8/22543b2ade9aa68fef028a9f0c4154bfdb970926f918f63d7b85bae527a9/alibabacloud_darabonba_encode_util-0.0.2.tar.gz", hash = "sha256:f1c484f276d60450fa49b4b2987194e741fcb2f7faae7f287c0ae65abc85fd4d", size = 3990, upload-time = "2022-12-10T04:43:48.086Z" } - -[[package]] -name = "alibabacloud-darabonba-number" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/cd/14bf39b6a509a2de4799477fd56a43fe478cab52971c89f6fbdc0d82dc85/alibabacloud_darabonba_number-0.0.4.tar.gz", hash = "sha256:c017fd5fb20bffb91a12a330be050524ae99add164662cb5466e85981faf3f34", size = 3036, upload-time = "2021-03-29T03:27:23.449Z" } - -[[package]] -name = "alibabacloud-darabonba-string" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/d4/3d22bd2ff88985f970a10f8cedf2ea326d11d4d95e244f7665dc83d26465/alibabacloud-darabonba-string-0.0.4.tar.gz", hash = "sha256:ec6614c0448dadcbc5e466485838a1f8cfdd911135bea739e20b14511270c6f7", size = 2852, upload-time = "2021-12-13T13:30:06.114Z" } - -[[package]] -name = "alibabacloud-darabonba-time" -version = "0.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/df/33a239384353130eef51af14cec1fa99058b2c5f4c7f3d0b283e5f17b874/alibabacloud_darabonba_time-0.0.1.tar.gz", hash = "sha256:0ad9c7b0696570d1a3f40106cc7777f755fd92baa0d1dcab5b7df78dde5b922d", size = 2885, upload-time = "2021-01-05T02:58:06.121Z" } - -[[package]] -name = "alibabacloud-ha3-util" -version = "0.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7a/c9/ccc6cafc071d242391d788a6a21effd3fb3fd5be782e005526191e3a9992/alibabacloud_ha3_util-0.0.1.tar.gz", hash = "sha256:1cbc59b53126bbfea82f06f812b6ea462d17a3433d1a79e558b5e64d092a7a24", size = 2348, upload-time = "2024-12-31T08:19:27.145Z" } - -[[package]] -name = "alibabacloud-ha3engine-vector" -version = "1.1.18" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "alibabacloud-darabonba-encode-util" }, - { name = "alibabacloud-darabonba-number" }, - { name = "alibabacloud-darabonba-string" }, - { name = "alibabacloud-darabonba-time" }, - { name = "alibabacloud-ha3-util" }, - { name = "alibabacloud-tea-util" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ae/d5/bfad642df8d2bd0e2124f2f4b0be9271914ba2ee637bc31d2f97f642c882/alibabacloud_ha3engine_vector-1.1.18.tar.gz", hash = "sha256:d1337b7499c04c09e014adaad2fa44f8c88e9e91c53b6c3579fc62b1bebceb5a", size = 18263, upload-time = "2026-04-02T08:25:27.776Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/a9/23fbe97dc5b5259a4b5a8b48fa865778ab3554acde82414fe2eee9cf1e59/alibabacloud_ha3engine_vector-1.1.18-py3-none-any.whl", hash = "sha256:92bf9b51b1743e8e3177e8b483a832dd6f62015d672ef6634de82ea4ebcd7fac", size = 17984, upload-time = "2026-04-02T08:25:26.957Z" }, -] - -[[package]] -name = "alibabacloud-tea" -version = "0.4.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9a/7d/b22cb9a0d4f396ee0f3f9d7f26b76b9ed93d4101add7867a2c87ed2534f5/alibabacloud-tea-0.4.3.tar.gz", hash = "sha256:ec8053d0aa8d43ebe1deb632d5c5404339b39ec9a18a0707d57765838418504a", size = 8785, upload-time = "2025-03-24T07:34:42.958Z" } - -[[package]] -name = "alibabacloud-tea-util" -version = "0.3.14" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "alibabacloud-tea" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/ee/ea90be94ad781a5055db29556744681fc71190ef444ae53adba45e1be5f3/alibabacloud_tea_util-0.3.14.tar.gz", hash = "sha256:708e7c9f64641a3c9e0e566365d2f23675f8d7c2a3e2971d9402ceede0408cdb", size = 7515, upload-time = "2025-11-19T06:01:08.504Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/9e/c394b4e2104766fb28a1e44e3ed36e4c7773b4d05c868e482be99d5635c9/alibabacloud_tea_util-0.3.14-py3-none-any.whl", hash = "sha256:10d3e5c340d8f7ec69dd27345eb2fc5a1dab07875742525edf07bbe86db93bfe", size = 6697, upload-time = "2025-11-19T06:01:07.355Z" }, -] - -[[package]] -name = "aliyun-python-sdk-core" -version = "2.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jmespath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3e/09/da9f58eb38b4fdb97ba6523274fbf445ef6a06be64b433693da8307b4bec/aliyun-python-sdk-core-2.16.0.tar.gz", hash = "sha256:651caad597eb39d4fad6cf85133dffe92837d53bdf62db9d8f37dab6508bb8f9", size = 449555, upload-time = "2024-10-09T06:01:01.762Z" } - -[[package]] -name = "aliyun-python-sdk-kms" -version = "2.16.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aliyun-python-sdk-core" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a8/2c/9877d0e6b18ecf246df671ac65a5d1d9fecbf85bdcb5d43efbde0d4662eb/aliyun-python-sdk-kms-2.16.5.tar.gz", hash = "sha256:f328a8a19d83ecbb965ffce0ec1e9930755216d104638cd95ecd362753b813b3", size = 12018, upload-time = "2024-08-30T09:01:20.104Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/5c/0132193d7da2c735669a1ed103b142fd63c9455984d48c5a88a1a516efaa/aliyun_python_sdk_kms-2.16.5-py2.py3-none-any.whl", hash = "sha256:24b6cdc4fd161d2942619479c8d050c63ea9cd22b044fe33b60bbb60153786f0", size = 99495, upload-time = "2024-08-30T09:01:18.462Z" }, -] - -[[package]] -name = "altair" -version = "5.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "narwhals" }, - { name = "packaging" }, - { name = "typing-extensions", marker = "python_full_version < '3.14'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/16/b1/f2969c7bdb8ad8bbdda031687defdce2c19afba2aa2c8e1d2a17f78376d8/altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d", size = 705305, upload-time = "2024-11-23T23:39:58.542Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/f3/0b6ced594e51cc95d8c1fc1640d3623770d01e4969d29c0bd09945fafefa/altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c", size = 731200, upload-time = "2024-11-23T23:39:56.4Z" }, -] - -[[package]] -name = "altex" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "altair" }, - { name = "pandas" }, - { name = "streamlit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/bc/200768a7faf5fcdbfefbefdbb1528a88fe552a1f6bf26f40097a22e854c7/altex-0.2.0.tar.gz", hash = "sha256:5ed6404af0d242a751111bd78421951d5e7025f9c559b7bcb151d3b4e63299d7", size = 100539, upload-time = "2025-06-30T17:02:33.437Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/ef/f94a52c48f8396f07411c8a0a244c4b551a6d57ecbcdc81d5d2345ebbd69/altex-0.2.0-py3-none-any.whl", hash = "sha256:b831e65f908f0197a1a092daab66c7059648ddfed2db8490bde6a331d524c85a", size = 25491, upload-time = "2025-06-30T17:02:32.343Z" }, -] - -[[package]] -name = "annotated-doc" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, -] - -[[package]] -name = "anyio" -version = "4.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, -] - -[[package]] -name = "async-timeout" -version = "5.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, -] - -[[package]] -name = "attrs" -version = "26.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, -] - -[[package]] -name = "authlib" -version = "1.6.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/af/98/00d3dd826d46959ad8e32af2dbb2398868fd9fd0683c26e56d0789bd0e68/authlib-1.6.9.tar.gz", hash = "sha256:d8f2421e7e5980cc1ddb4e32d3f5fa659cfaf60d8eaf3281ebed192e4ab74f04", size = 165134, upload-time = "2026-03-02T07:44:01.998Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/23/b65f568ed0c22f1efacb744d2db1a33c8068f384b8c9b482b52ebdbc3ef6/authlib-1.6.9-py2.py3-none-any.whl", hash = "sha256:f08b4c14e08f0861dc18a32357b33fbcfd2ea86cfe3fe149484b4d764c4a0ac3", size = 244197, upload-time = "2026-03-02T07:44:00.307Z" }, -] - -[[package]] -name = "backoff" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, -] - -[[package]] -name = "bcrypt" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/36/3329e2518d70ad8e2e5817d5a4cac6bba05a47767ec416c7d020a965f408/bcrypt-5.0.0.tar.gz", hash = "sha256:f748f7c2d6fd375cc93d3fba7ef4a9e3a092421b8dbf34d8d4dc06be9492dfdd", size = 25386, upload-time = "2025-09-25T19:50:47.829Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/85/3e65e01985fddf25b64ca67275bb5bdb4040bd1a53b66d355c6c37c8a680/bcrypt-5.0.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f3c08197f3039bec79cee59a606d62b96b16669cff3949f21e74796b6e3cd2be", size = 481806, upload-time = "2025-09-25T19:49:05.102Z" }, - { url = "https://files.pythonhosted.org/packages/44/dc/01eb79f12b177017a726cbf78330eb0eb442fae0e7b3dfd84ea2849552f3/bcrypt-5.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:200af71bc25f22006f4069060c88ed36f8aa4ff7f53e67ff04d2ab3f1e79a5b2", size = 268626, upload-time = "2025-09-25T19:49:06.723Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/e82388ad5959c40d6afd94fb4743cc077129d45b952d46bdc3180310e2df/bcrypt-5.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:baade0a5657654c2984468efb7d6c110db87ea63ef5a4b54732e7e337253e44f", size = 271853, upload-time = "2025-09-25T19:49:08.028Z" }, - { url = "https://files.pythonhosted.org/packages/ec/86/7134b9dae7cf0efa85671651341f6afa695857fae172615e960fb6a466fa/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c58b56cdfb03202b3bcc9fd8daee8e8e9b6d7e3163aa97c631dfcfcc24d36c86", size = 269793, upload-time = "2025-09-25T19:49:09.727Z" }, - { url = "https://files.pythonhosted.org/packages/cc/82/6296688ac1b9e503d034e7d0614d56e80c5d1a08402ff856a4549cb59207/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4bfd2a34de661f34d0bda43c3e4e79df586e4716ef401fe31ea39d69d581ef23", size = 289930, upload-time = "2025-09-25T19:49:11.204Z" }, - { url = "https://files.pythonhosted.org/packages/d1/18/884a44aa47f2a3b88dd09bc05a1e40b57878ecd111d17e5bba6f09f8bb77/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ed2e1365e31fc73f1825fa830f1c8f8917ca1b3ca6185773b349c20fd606cec2", size = 272194, upload-time = "2025-09-25T19:49:12.524Z" }, - { url = "https://files.pythonhosted.org/packages/0e/8f/371a3ab33c6982070b674f1788e05b656cfbf5685894acbfef0c65483a59/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:83e787d7a84dbbfba6f250dd7a5efd689e935f03dd83b0f919d39349e1f23f83", size = 269381, upload-time = "2025-09-25T19:49:14.308Z" }, - { url = "https://files.pythonhosted.org/packages/b1/34/7e4e6abb7a8778db6422e88b1f06eb07c47682313997ee8a8f9352e5a6f1/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:137c5156524328a24b9fac1cb5db0ba618bc97d11970b39184c1d87dc4bf1746", size = 271750, upload-time = "2025-09-25T19:49:15.584Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1b/54f416be2499bd72123c70d98d36c6cd61a4e33d9b89562c22481c81bb30/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:38cac74101777a6a7d3b3e3cfefa57089b5ada650dce2baf0cbdd9d65db22a9e", size = 303757, upload-time = "2025-09-25T19:49:17.244Z" }, - { url = "https://files.pythonhosted.org/packages/13/62/062c24c7bcf9d2826a1a843d0d605c65a755bc98002923d01fd61270705a/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:d8d65b564ec849643d9f7ea05c6d9f0cd7ca23bdd4ac0c2dbef1104ab504543d", size = 306740, upload-time = "2025-09-25T19:49:18.693Z" }, - { url = "https://files.pythonhosted.org/packages/d5/c8/1fdbfc8c0f20875b6b4020f3c7dc447b8de60aa0be5faaf009d24242aec9/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:741449132f64b3524e95cd30e5cd3343006ce146088f074f31ab26b94e6c75ba", size = 334197, upload-time = "2025-09-25T19:49:20.523Z" }, - { url = "https://files.pythonhosted.org/packages/a6/c1/8b84545382d75bef226fbc6588af0f7b7d095f7cd6a670b42a86243183cd/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:212139484ab3207b1f0c00633d3be92fef3c5f0af17cad155679d03ff2ee1e41", size = 352974, upload-time = "2025-09-25T19:49:22.254Z" }, - { url = "https://files.pythonhosted.org/packages/10/a6/ffb49d4254ed085e62e3e5dd05982b4393e32fe1e49bb1130186617c29cd/bcrypt-5.0.0-cp313-cp313t-win32.whl", hash = "sha256:9d52ed507c2488eddd6a95bccee4e808d3234fa78dd370e24bac65a21212b861", size = 148498, upload-time = "2025-09-25T19:49:24.134Z" }, - { url = "https://files.pythonhosted.org/packages/48/a9/259559edc85258b6d5fc5471a62a3299a6aa37a6611a169756bf4689323c/bcrypt-5.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f6984a24db30548fd39a44360532898c33528b74aedf81c26cf29c51ee47057e", size = 145853, upload-time = "2025-09-25T19:49:25.702Z" }, - { url = "https://files.pythonhosted.org/packages/2d/df/9714173403c7e8b245acf8e4be8876aac64a209d1b392af457c79e60492e/bcrypt-5.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9fffdb387abe6aa775af36ef16f55e318dcda4194ddbf82007a6f21da29de8f5", size = 139626, upload-time = "2025-09-25T19:49:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/f8/14/c18006f91816606a4abe294ccc5d1e6f0e42304df5a33710e9e8e95416e1/bcrypt-5.0.0-cp314-cp314t-macosx_10_12_universal2.whl", hash = "sha256:4870a52610537037adb382444fefd3706d96d663ac44cbb2f37e3919dca3d7ef", size = 481862, upload-time = "2025-09-25T19:49:28.365Z" }, - { url = "https://files.pythonhosted.org/packages/67/49/dd074d831f00e589537e07a0725cf0e220d1f0d5d8e85ad5bbff251c45aa/bcrypt-5.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48f753100931605686f74e27a7b49238122aa761a9aefe9373265b8b7aa43ea4", size = 268544, upload-time = "2025-09-25T19:49:30.39Z" }, - { url = "https://files.pythonhosted.org/packages/f5/91/50ccba088b8c474545b034a1424d05195d9fcbaaf802ab8bfe2be5a4e0d7/bcrypt-5.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f70aadb7a809305226daedf75d90379c397b094755a710d7014b8b117df1ebbf", size = 271787, upload-time = "2025-09-25T19:49:32.144Z" }, - { url = "https://files.pythonhosted.org/packages/aa/e7/d7dba133e02abcda3b52087a7eea8c0d4f64d3e593b4fffc10c31b7061f3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:744d3c6b164caa658adcb72cb8cc9ad9b4b75c7db507ab4bc2480474a51989da", size = 269753, upload-time = "2025-09-25T19:49:33.885Z" }, - { url = "https://files.pythonhosted.org/packages/33/fc/5b145673c4b8d01018307b5c2c1fc87a6f5a436f0ad56607aee389de8ee3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a28bc05039bdf3289d757f49d616ab3efe8cf40d8e8001ccdd621cd4f98f4fc9", size = 289587, upload-time = "2025-09-25T19:49:35.144Z" }, - { url = "https://files.pythonhosted.org/packages/27/d7/1ff22703ec6d4f90e62f1a5654b8867ef96bafb8e8102c2288333e1a6ca6/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7f277a4b3390ab4bebe597800a90da0edae882c6196d3038a73adf446c4f969f", size = 272178, upload-time = "2025-09-25T19:49:36.793Z" }, - { url = "https://files.pythonhosted.org/packages/c8/88/815b6d558a1e4d40ece04a2f84865b0fef233513bd85fd0e40c294272d62/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:79cfa161eda8d2ddf29acad370356b47f02387153b11d46042e93a0a95127493", size = 269295, upload-time = "2025-09-25T19:49:38.164Z" }, - { url = "https://files.pythonhosted.org/packages/51/8c/e0db387c79ab4931fc89827d37608c31cc57b6edc08ccd2386139028dc0d/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a5393eae5722bcef046a990b84dff02b954904c36a194f6cfc817d7dca6c6f0b", size = 271700, upload-time = "2025-09-25T19:49:39.917Z" }, - { url = "https://files.pythonhosted.org/packages/06/83/1570edddd150f572dbe9fc00f6203a89fc7d4226821f67328a85c330f239/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f4c94dec1b5ab5d522750cb059bb9409ea8872d4494fd152b53cca99f1ddd8c", size = 334034, upload-time = "2025-09-25T19:49:41.227Z" }, - { url = "https://files.pythonhosted.org/packages/c9/f2/ea64e51a65e56ae7a8a4ec236c2bfbdd4b23008abd50ac33fbb2d1d15424/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0cae4cb350934dfd74c020525eeae0a5f79257e8a201c0c176f4b84fdbf2a4b4", size = 352766, upload-time = "2025-09-25T19:49:43.08Z" }, - { url = "https://files.pythonhosted.org/packages/d7/d4/1a388d21ee66876f27d1a1f41287897d0c0f1712ef97d395d708ba93004c/bcrypt-5.0.0-cp314-cp314t-win32.whl", hash = "sha256:b17366316c654e1ad0306a6858e189fc835eca39f7eb2cafd6aaca8ce0c40a2e", size = 152449, upload-time = "2025-09-25T19:49:44.971Z" }, - { url = "https://files.pythonhosted.org/packages/3f/61/3291c2243ae0229e5bca5d19f4032cecad5dfb05a2557169d3a69dc0ba91/bcrypt-5.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:92864f54fb48b4c718fc92a32825d0e42265a627f956bc0361fe869f1adc3e7d", size = 149310, upload-time = "2025-09-25T19:49:46.162Z" }, - { url = "https://files.pythonhosted.org/packages/3e/89/4b01c52ae0c1a681d4021e5dd3e45b111a8fb47254a274fa9a378d8d834b/bcrypt-5.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dd19cf5184a90c873009244586396a6a884d591a5323f0e8a5922560718d4993", size = 143761, upload-time = "2025-09-25T19:49:47.345Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/6237f151fbfe295fe3e074ecc6d44228faa1e842a81f6d34a02937ee1736/bcrypt-5.0.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:fc746432b951e92b58317af8e0ca746efe93e66555f1b40888865ef5bf56446b", size = 494553, upload-time = "2025-09-25T19:49:49.006Z" }, - { url = "https://files.pythonhosted.org/packages/45/b6/4c1205dde5e464ea3bd88e8742e19f899c16fa8916fb8510a851fae985b5/bcrypt-5.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c2388ca94ffee269b6038d48747f4ce8df0ffbea43f31abfa18ac72f0218effb", size = 275009, upload-time = "2025-09-25T19:49:50.581Z" }, - { url = "https://files.pythonhosted.org/packages/3b/71/427945e6ead72ccffe77894b2655b695ccf14ae1866cd977e185d606dd2f/bcrypt-5.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:560ddb6ec730386e7b3b26b8b4c88197aaed924430e7b74666a586ac997249ef", size = 278029, upload-time = "2025-09-25T19:49:52.533Z" }, - { url = "https://files.pythonhosted.org/packages/17/72/c344825e3b83c5389a369c8a8e58ffe1480b8a699f46c127c34580c4666b/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d79e5c65dcc9af213594d6f7f1fa2c98ad3fc10431e7aa53c176b441943efbdd", size = 275907, upload-time = "2025-09-25T19:49:54.709Z" }, - { url = "https://files.pythonhosted.org/packages/0b/7e/d4e47d2df1641a36d1212e5c0514f5291e1a956a7749f1e595c07a972038/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2b732e7d388fa22d48920baa267ba5d97cca38070b69c0e2d37087b381c681fd", size = 296500, upload-time = "2025-09-25T19:49:56.013Z" }, - { url = "https://files.pythonhosted.org/packages/0f/c3/0ae57a68be2039287ec28bc463b82e4b8dc23f9d12c0be331f4782e19108/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0c8e093ea2532601a6f686edbc2c6b2ec24131ff5c52f7610dd64fa4553b5464", size = 278412, upload-time = "2025-09-25T19:49:57.356Z" }, - { url = "https://files.pythonhosted.org/packages/45/2b/77424511adb11e6a99e3a00dcc7745034bee89036ad7d7e255a7e47be7d8/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5b1589f4839a0899c146e8892efe320c0fa096568abd9b95593efac50a87cb75", size = 275486, upload-time = "2025-09-25T19:49:59.116Z" }, - { url = "https://files.pythonhosted.org/packages/43/0a/405c753f6158e0f3f14b00b462d8bca31296f7ecfc8fc8bc7919c0c7d73a/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:89042e61b5e808b67daf24a434d89bab164d4de1746b37a8d173b6b14f3db9ff", size = 277940, upload-time = "2025-09-25T19:50:00.869Z" }, - { url = "https://files.pythonhosted.org/packages/62/83/b3efc285d4aadc1fa83db385ec64dcfa1707e890eb42f03b127d66ac1b7b/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e3cf5b2560c7b5a142286f69bde914494b6d8f901aaa71e453078388a50881c4", size = 310776, upload-time = "2025-09-25T19:50:02.393Z" }, - { url = "https://files.pythonhosted.org/packages/95/7d/47ee337dacecde6d234890fe929936cb03ebc4c3a7460854bbd9c97780b8/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f632fd56fc4e61564f78b46a2269153122db34988e78b6be8b32d28507b7eaeb", size = 312922, upload-time = "2025-09-25T19:50:04.232Z" }, - { url = "https://files.pythonhosted.org/packages/d6/3a/43d494dfb728f55f4e1cf8fd435d50c16a2d75493225b54c8d06122523c6/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:801cad5ccb6b87d1b430f183269b94c24f248dddbbc5c1f78b6ed231743e001c", size = 341367, upload-time = "2025-09-25T19:50:05.559Z" }, - { url = "https://files.pythonhosted.org/packages/55/ab/a0727a4547e383e2e22a630e0f908113db37904f58719dc48d4622139b5c/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3cf67a804fc66fc217e6914a5635000259fbbbb12e78a99488e4d5ba445a71eb", size = 359187, upload-time = "2025-09-25T19:50:06.916Z" }, - { url = "https://files.pythonhosted.org/packages/1b/bb/461f352fdca663524b4643d8b09e8435b4990f17fbf4fea6bc2a90aa0cc7/bcrypt-5.0.0-cp38-abi3-win32.whl", hash = "sha256:3abeb543874b2c0524ff40c57a4e14e5d3a66ff33fb423529c88f180fd756538", size = 153752, upload-time = "2025-09-25T19:50:08.515Z" }, - { url = "https://files.pythonhosted.org/packages/41/aa/4190e60921927b7056820291f56fc57d00d04757c8b316b2d3c0d1d6da2c/bcrypt-5.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:35a77ec55b541e5e583eb3436ffbbf53b0ffa1fa16ca6782279daf95d146dcd9", size = 150881, upload-time = "2025-09-25T19:50:09.742Z" }, - { url = "https://files.pythonhosted.org/packages/54/12/cd77221719d0b39ac0b55dbd39358db1cd1246e0282e104366ebbfb8266a/bcrypt-5.0.0-cp38-abi3-win_arm64.whl", hash = "sha256:cde08734f12c6a4e28dc6755cd11d3bdfea608d93d958fffbe95a7026ebe4980", size = 144931, upload-time = "2025-09-25T19:50:11.016Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ba/2af136406e1c3839aea9ecadc2f6be2bcd1eff255bd451dd39bcf302c47a/bcrypt-5.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0c418ca99fd47e9c59a301744d63328f17798b5947b0f791e9af3c1c499c2d0a", size = 495313, upload-time = "2025-09-25T19:50:12.309Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ee/2f4985dbad090ace5ad1f7dd8ff94477fe089b5fab2040bd784a3d5f187b/bcrypt-5.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddb4e1500f6efdd402218ffe34d040a1196c072e07929b9820f363a1fd1f4191", size = 275290, upload-time = "2025-09-25T19:50:13.673Z" }, - { url = "https://files.pythonhosted.org/packages/e4/6e/b77ade812672d15cf50842e167eead80ac3514f3beacac8902915417f8b7/bcrypt-5.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7aeef54b60ceddb6f30ee3db090351ecf0d40ec6e2abf41430997407a46d2254", size = 278253, upload-time = "2025-09-25T19:50:15.089Z" }, - { url = "https://files.pythonhosted.org/packages/36/c4/ed00ed32f1040f7990dac7115f82273e3c03da1e1a1587a778d8cea496d8/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f0ce778135f60799d89c9693b9b398819d15f1921ba15fe719acb3178215a7db", size = 276084, upload-time = "2025-09-25T19:50:16.699Z" }, - { url = "https://files.pythonhosted.org/packages/e7/c4/fa6e16145e145e87f1fa351bbd54b429354fd72145cd3d4e0c5157cf4c70/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a71f70ee269671460b37a449f5ff26982a6f2ba493b3eabdd687b4bf35f875ac", size = 297185, upload-time = "2025-09-25T19:50:18.525Z" }, - { url = "https://files.pythonhosted.org/packages/24/b4/11f8a31d8b67cca3371e046db49baa7c0594d71eb40ac8121e2fc0888db0/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8429e1c410b4073944f03bd778a9e066e7fad723564a52ff91841d278dfc822", size = 278656, upload-time = "2025-09-25T19:50:19.809Z" }, - { url = "https://files.pythonhosted.org/packages/ac/31/79f11865f8078e192847d2cb526e3fa27c200933c982c5b2869720fa5fce/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:edfcdcedd0d0f05850c52ba3127b1fce70b9f89e0fe5ff16517df7e81fa3cbb8", size = 275662, upload-time = "2025-09-25T19:50:21.567Z" }, - { url = "https://files.pythonhosted.org/packages/d4/8d/5e43d9584b3b3591a6f9b68f755a4da879a59712981ef5ad2a0ac1379f7a/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:611f0a17aa4a25a69362dcc299fda5c8a3d4f160e2abb3831041feb77393a14a", size = 278240, upload-time = "2025-09-25T19:50:23.305Z" }, - { url = "https://files.pythonhosted.org/packages/89/48/44590e3fc158620f680a978aafe8f87a4c4320da81ed11552f0323aa9a57/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:db99dca3b1fdc3db87d7c57eac0c82281242d1eabf19dcb8a6b10eb29a2e72d1", size = 311152, upload-time = "2025-09-25T19:50:24.597Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/e4fbfc46f14f47b0d20493669a625da5827d07e8a88ee460af6cd9768b44/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:5feebf85a9cefda32966d8171f5db7e3ba964b77fdfe31919622256f80f9cf42", size = 313284, upload-time = "2025-09-25T19:50:26.268Z" }, - { url = "https://files.pythonhosted.org/packages/25/ae/479f81d3f4594456a01ea2f05b132a519eff9ab5768a70430fa1132384b1/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3ca8a166b1140436e058298a34d88032ab62f15aae1c598580333dc21d27ef10", size = 341643, upload-time = "2025-09-25T19:50:28.02Z" }, - { url = "https://files.pythonhosted.org/packages/df/d2/36a086dee1473b14276cd6ea7f61aef3b2648710b5d7f1c9e032c29b859f/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:61afc381250c3182d9078551e3ac3a41da14154fbff647ddf52a769f588c4172", size = 359698, upload-time = "2025-09-25T19:50:31.347Z" }, - { url = "https://files.pythonhosted.org/packages/c0/f6/688d2cd64bfd0b14d805ddb8a565e11ca1fb0fd6817175d58b10052b6d88/bcrypt-5.0.0-cp39-abi3-win32.whl", hash = "sha256:64d7ce196203e468c457c37ec22390f1a61c85c6f0b8160fd752940ccfb3a683", size = 153725, upload-time = "2025-09-25T19:50:34.384Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b9/9d9a641194a730bda138b3dfe53f584d61c58cd5230e37566e83ec2ffa0d/bcrypt-5.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:64ee8434b0da054d830fa8e89e1c8bf30061d539044a39524ff7dec90481e5c2", size = 150912, upload-time = "2025-09-25T19:50:35.69Z" }, - { url = "https://files.pythonhosted.org/packages/27/44/d2ef5e87509158ad2187f4dd0852df80695bb1ee0cfe0a684727b01a69e0/bcrypt-5.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:f2347d3534e76bf50bca5500989d6c1d05ed64b440408057a37673282c654927", size = 144953, upload-time = "2025-09-25T19:50:37.32Z" }, - { url = "https://files.pythonhosted.org/packages/8a/75/4aa9f5a4d40d762892066ba1046000b329c7cd58e888a6db878019b282dc/bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7edda91d5ab52b15636d9c30da87d2cc84f426c72b9dba7a9b4fe142ba11f534", size = 271180, upload-time = "2025-09-25T19:50:38.575Z" }, - { url = "https://files.pythonhosted.org/packages/54/79/875f9558179573d40a9cc743038ac2bf67dfb79cecb1e8b5d70e88c94c3d/bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:046ad6db88edb3c5ece4369af997938fb1c19d6a699b9c1b27b0db432faae4c4", size = 273791, upload-time = "2025-09-25T19:50:39.913Z" }, - { url = "https://files.pythonhosted.org/packages/bc/fe/975adb8c216174bf70fc17535f75e85ac06ed5252ea077be10d9cff5ce24/bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:dcd58e2b3a908b5ecc9b9df2f0085592506ac2d5110786018ee5e160f28e0911", size = 270746, upload-time = "2025-09-25T19:50:43.306Z" }, - { url = "https://files.pythonhosted.org/packages/e4/f8/972c96f5a2b6c4b3deca57009d93e946bbdbe2241dca9806d502f29dd3ee/bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:6b8f520b61e8781efee73cba14e3e8c9556ccfb375623f4f97429544734545b4", size = 273375, upload-time = "2025-09-25T19:50:45.43Z" }, -] - -[[package]] -name = "beautifulsoup4" -version = "4.14.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "soupsieve" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, -] - -[[package]] -name = "black" -version = "26.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "mypy-extensions" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, - { name = "pytokens" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/c5/61175d618685d42b005847464b8fb4743a67b1b8fdb75e50e5a96c31a27a/black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07", size = 666155, upload-time = "2026-03-12T03:36:03.593Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/57/5f11c92861f9c92eb9dddf515530bc2d06db843e44bdcf1c83c1427824bc/black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff", size = 1851987, upload-time = "2026-03-12T03:40:06.248Z" }, - { url = "https://files.pythonhosted.org/packages/54/aa/340a1463660bf6831f9e39646bf774086dbd8ca7fc3cded9d59bbdf4ad0a/black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c", size = 1689499, upload-time = "2026-03-12T03:40:07.642Z" }, - { url = "https://files.pythonhosted.org/packages/f3/01/b726c93d717d72733da031d2de10b92c9fa4c8d0c67e8a8a372076579279/black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5", size = 1754369, upload-time = "2026-03-12T03:40:09.279Z" }, - { url = "https://files.pythonhosted.org/packages/e3/09/61e91881ca291f150cfc9eb7ba19473c2e59df28859a11a88248b5cbbc4d/black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e", size = 1413613, upload-time = "2026-03-12T03:40:10.943Z" }, - { url = "https://files.pythonhosted.org/packages/16/73/544f23891b22e7efe4d8f812371ab85b57f6a01b2fc45e3ba2e52ba985b8/black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5", size = 1219719, upload-time = "2026-03-12T03:40:12.597Z" }, - { url = "https://files.pythonhosted.org/packages/dc/f8/da5eae4fc75e78e6dceb60624e1b9662ab00d6b452996046dfa9b8a6025b/black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1", size = 1895920, upload-time = "2026-03-12T03:40:13.921Z" }, - { url = "https://files.pythonhosted.org/packages/2c/9f/04e6f26534da2e1629b2b48255c264cabf5eedc5141d04516d9d68a24111/black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f", size = 1718499, upload-time = "2026-03-12T03:40:15.239Z" }, - { url = "https://files.pythonhosted.org/packages/04/91/a5935b2a63e31b331060c4a9fdb5a6c725840858c599032a6f3aac94055f/black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7", size = 1794994, upload-time = "2026-03-12T03:40:17.124Z" }, - { url = "https://files.pythonhosted.org/packages/e7/0a/86e462cdd311a3c2a8ece708d22aba17d0b2a0d5348ca34b40cdcbea512e/black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983", size = 1420867, upload-time = "2026-03-12T03:40:18.83Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e5/22515a19cb7eaee3440325a6b0d95d2c0e88dd180cb011b12ae488e031d1/black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb", size = 1230124, upload-time = "2026-03-12T03:40:20.425Z" }, - { url = "https://files.pythonhosted.org/packages/f5/77/5728052a3c0450c53d9bb3945c4c46b91baa62b2cafab6801411b6271e45/black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54", size = 1895034, upload-time = "2026-03-12T03:40:21.813Z" }, - { url = "https://files.pythonhosted.org/packages/52/73/7cae55fdfdfbe9d19e9a8d25d145018965fe2079fa908101c3733b0c55a0/black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f", size = 1718503, upload-time = "2026-03-12T03:40:23.666Z" }, - { url = "https://files.pythonhosted.org/packages/e1/87/af89ad449e8254fdbc74654e6467e3c9381b61472cc532ee350d28cfdafb/black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56", size = 1793557, upload-time = "2026-03-12T03:40:25.497Z" }, - { url = "https://files.pythonhosted.org/packages/43/10/d6c06a791d8124b843bf325ab4ac7d2f5b98731dff84d6064eafd687ded1/black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839", size = 1422766, upload-time = "2026-03-12T03:40:27.14Z" }, - { url = "https://files.pythonhosted.org/packages/59/4f/40a582c015f2d841ac24fed6390bd68f0fc896069ff3a886317959c9daf8/black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2", size = 1232140, upload-time = "2026-03-12T03:40:28.882Z" }, - { url = "https://files.pythonhosted.org/packages/d5/da/e36e27c9cebc1311b7579210df6f1c86e50f2d7143ae4fcf8a5017dc8809/black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78", size = 1889234, upload-time = "2026-03-12T03:40:30.964Z" }, - { url = "https://files.pythonhosted.org/packages/0e/7b/9871acf393f64a5fa33668c19350ca87177b181f44bb3d0c33b2d534f22c/black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568", size = 1720522, upload-time = "2026-03-12T03:40:32.346Z" }, - { url = "https://files.pythonhosted.org/packages/03/87/e766c7f2e90c07fb7586cc787c9ae6462b1eedab390191f2b7fc7f6170a9/black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f", size = 1787824, upload-time = "2026-03-12T03:40:33.636Z" }, - { url = "https://files.pythonhosted.org/packages/ac/94/2424338fb2d1875e9e83eed4c8e9c67f6905ec25afd826a911aea2b02535/black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c", size = 1445855, upload-time = "2026-03-12T03:40:35.442Z" }, - { url = "https://files.pythonhosted.org/packages/86/43/0c3338bd928afb8ee7471f1a4eec3bdbe2245ccb4a646092a222e8669840/black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1", size = 1258109, upload-time = "2026-03-12T03:40:36.832Z" }, - { url = "https://files.pythonhosted.org/packages/8e/0d/52d98722666d6fc6c3dd4c76df339501d6efd40e0ff95e6186a7b7f0befd/black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b", size = 207542, upload-time = "2026-03-12T03:36:01.668Z" }, -] - -[[package]] -name = "blinker" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, -] - -[[package]] -name = "botocore" -version = "1.42.84" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jmespath" }, - { name = "python-dateutil" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b4/b7/1c03423843fb0d1795b686511c00ee63fed1234c2400f469aeedfd42212f/botocore-1.42.84.tar.gz", hash = "sha256:234064604c80d9272a5e9f6b3566d260bcaa053a5e05246db90d7eca1c2cf44b", size = 15148615, upload-time = "2026-04-06T19:38:56.673Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/37/0c0c90361c8a1b9e6c75222ca24ae12996a298c0e18822a72ab229c37207/botocore-1.42.84-py3-none-any.whl", hash = "sha256:15f3fe07dfa6545e46a60c4b049fe2bdf63803c595ae4a4eec90e8f8172764f3", size = 14827061, upload-time = "2026-04-06T19:38:53.613Z" }, -] - -[[package]] -name = "cachetools" -version = "5.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, -] - -[[package]] -name = "certifi" -version = "2026.2.25" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, - { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, - { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, - { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, - { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, - { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, - { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, - { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, - { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, - { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, - { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, - { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, - { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, - { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, - { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, - { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, - { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, - { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, - { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, - { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, - { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, - { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, - { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, - { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, - { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, - { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, - { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, - { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, - { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, - { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, - { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, - { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, - { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, - { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, - { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, - { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, - { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, - { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, - { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, - { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, - { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, -] - -[[package]] -name = "chroma-hnswlib" -version = "0.7.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/59/1224cbae62c7b84c84088cdf6c106b9b2b893783c000d22c442a1672bc75/chroma-hnswlib-0.7.3.tar.gz", hash = "sha256:b6137bedde49fffda6af93b0297fe00429fc61e5a072b1ed9377f909ed95a932", size = 31876, upload-time = "2023-09-11T15:52:00.902Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/3f/844393b0d2ea1072b7704d6eff5c595e05ae8b831b96340cdb76b2fe995c/chroma_hnswlib-0.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:11e7ca93fb8192214ac2b9c0943641ac0daf8f9d4591bb7b73be808a83835667", size = 221219, upload-time = "2023-09-11T15:51:27.014Z" }, - { url = "https://files.pythonhosted.org/packages/11/7a/673ccb9bb2faf9cf655d9040e970c02a96645966e06837fde7d10edf242a/chroma_hnswlib-0.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6f552e4d23edc06cdeb553cdc757d2fe190cdeb10d43093d6a3319f8d4bf1c6b", size = 198652, upload-time = "2023-09-11T15:51:28.806Z" }, - { url = "https://files.pythonhosted.org/packages/ba/f4/c81a40da5473d5d80fc9d0c5bd5b1cb64e530a6ea941c69f195fe81c488c/chroma_hnswlib-0.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f96f4d5699e486eb1fb95849fe35ab79ab0901265805be7e60f4eaa83ce263ec", size = 2332260, upload-time = "2023-09-11T15:51:31.034Z" }, - { url = "https://files.pythonhosted.org/packages/48/0e/068b658a547d6090b969014146321e28dae1411da54b76d081e51a2af22b/chroma_hnswlib-0.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:368e57fe9ebae05ee5844840fa588028a023d1182b0cfdb1d13f607c9ea05756", size = 2367211, upload-time = "2023-09-11T15:51:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/d2/32/a91850c7aa8a34f61838913155103808fe90da6f1ea4302731b59e9ba6f2/chroma_hnswlib-0.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:b7dca27b8896b494456db0fd705b689ac6b73af78e186eb6a42fea2de4f71c6f", size = 151574, upload-time = "2023-09-11T15:51:34.311Z" }, -] - -[[package]] -name = "chromadb" -version = "0.4.13" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "bcrypt" }, - { name = "chroma-hnswlib" }, - { name = "fastapi" }, - { name = "importlib-resources" }, - { name = "numpy" }, - { name = "onnxruntime" }, - { name = "overrides" }, - { name = "posthog" }, - { name = "pulsar-client" }, - { name = "pydantic" }, - { name = "pypika" }, - { name = "requests" }, - { name = "tokenizers" }, - { name = "tqdm" }, - { name = "typer" }, - { name = "typing-extensions" }, - { name = "uvicorn", extra = ["standard"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/a5/8a61c3624e5c70f6cc3c1413009100b58a794baf4c60599f84f681e6bcf6/chromadb-0.4.13.tar.gz", hash = "sha256:99d330b9ac8f2ec81f4b34798d34f2ea9f4656bef1da951efa7e93957ef7e706", size = 1276642, upload-time = "2023-09-25T17:58:16.728Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/58/7bf23f206b8ad9507295067f08b7cc42d4a91d9877301abf0807df8fbe67/chromadb-0.4.13-py3-none-any.whl", hash = "sha256:6959dc4aaa6278c7491dd1911724981a0e46816b19e9f86945b9bd875e6a252a", size = 437831, upload-time = "2023-09-25T17:58:14.308Z" }, -] - -[[package]] -name = "click" -version = "8.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", size = 302856, upload-time = "2026-04-03T19:14:45.118Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" }, -] - -[[package]] -name = "clickhouse-connect" -version = "0.15.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "lz4" }, - { name = "pytz" }, - { name = "urllib3" }, - { name = "zstandard" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a5/b1/a17eb4409e2741286ccdac06b6ea15db178cdf1f0ed997bbf9ad3448f78e/clickhouse_connect-0.15.1.tar.gz", hash = "sha256:f2aaf5fc0bb3098c24f0d8ca7e4ecbe605a26957481dfca2c8cef9d1fad7b7ca", size = 126840, upload-time = "2026-03-30T18:58:31.113Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/6b/d9c222683e01469c14262a4f924c94ede359b654f656e8b993e0cf8ccaf4/clickhouse_connect-0.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9610ef6ff653f8a030f50e39cdeb1a39bea925c48f9196d787ea4b9f5eb1c8f0", size = 285313, upload-time = "2026-03-30T18:57:09.033Z" }, - { url = "https://files.pythonhosted.org/packages/66/cf/27d6ee5a15c0211bd13a1bc2b516b45e8b16055e877d3375f0c8cbbbd78a/clickhouse_connect-0.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cd41ebe8b7f1c2579b22bbc414a800f3f8f5c843928019aca27c81592f70c5a7", size = 278360, upload-time = "2026-03-30T18:57:10.338Z" }, - { url = "https://files.pythonhosted.org/packages/7d/83/d7d727a011580be3495dfb113d5a29f67745b1f7133aac87d19e205fa0c4/clickhouse_connect-0.15.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ff5d10c6e49d36ee6941f52c4233f2bfb4198e9c726fed224f725974a667e37", size = 1097483, upload-time = "2026-03-30T18:57:11.504Z" }, - { url = "https://files.pythonhosted.org/packages/01/1b/b69dde105ba12b8f3cb9bb7e36c3aae76a81d447a4b32294b036aa31aa3f/clickhouse_connect-0.15.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99d55aab64fdeb53d74c16d2c46ae5491e90aa37ba55c24884a68a869418ee8e", size = 1102402, upload-time = "2026-03-30T18:57:12.63Z" }, - { url = "https://files.pythonhosted.org/packages/d5/fb/1cfea665564a232f9a79e271c1c58ec2b627c920f1a47483eed720eba307/clickhouse_connect-0.15.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:57ad606e878fd284242713449217a0c475fde6b9b7ab59e7ba9e9c388431f004", size = 1084076, upload-time = "2026-03-30T18:57:13.89Z" }, - { url = "https://files.pythonhosted.org/packages/5e/ad/80df558d79dd85a493948dd10f6ec1c95a029d7aa78725b35af9726b2131/clickhouse_connect-0.15.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:693a03e44256886ac5dd26dc708833913157ec72e3b3a44fb89fd5fc202f85dc", size = 1106768, upload-time = "2026-03-30T18:57:15.155Z" }, - { url = "https://files.pythonhosted.org/packages/57/c9/322f0e85e477d46b7eca58dd6b7b49668dafa574237a4a724db411df5552/clickhouse_connect-0.15.1-cp311-cp311-win32.whl", hash = "sha256:f03814b6e6a72892ce913eaef3931e6d011068480e9c19b80e5c640fdac55109", size = 257103, upload-time = "2026-03-30T18:57:16.494Z" }, - { url = "https://files.pythonhosted.org/packages/d4/4a/67bb7a8423b7ab2f26ec8a12b14f276a07be8ca113a81d40f88d31bb5df6/clickhouse_connect-0.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:f13c34ad1ddb0d1efc92bc4039b50b534da94c51bbce25e61484bfd28b231cb5", size = 275290, upload-time = "2026-03-30T18:57:17.566Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b6/d0881ac34617b13ad555a4749aae042e0242bedbf8a258373719089885cd/clickhouse_connect-0.15.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0bef871fb9803ae82b4dc1f797b6e784de0a4dec351591191a0c1a6008548284", size = 287187, upload-time = "2026-03-30T18:57:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/d6/6e/27823c38e54247ea22d96b3f4fde32831a10e5203761c0e2893bc2fc587f/clickhouse_connect-0.15.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:df93fa024d6ed46dbc3182b6202180be4cf2bbe9c331dcb21f85963b1b3fd1e5", size = 278086, upload-time = "2026-03-30T18:57:20.104Z" }, - { url = "https://files.pythonhosted.org/packages/6a/88/f1096e8b4f08e628674490e5d186c7bf09174bbbc5fefa530e28e6b39da3/clickhouse_connect-0.15.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6e98c0cf53db3b24dc0ff9f522fcf13205b1d191c632567d1744fbd4671741f", size = 1122144, upload-time = "2026-03-30T18:57:21.205Z" }, - { url = "https://files.pythonhosted.org/packages/af/e5/027f8b94b54a39dcdf9b314a7cd66cb882d8ba166efc584908997c6d5acb/clickhouse_connect-0.15.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bf70933ab860bd2f0a872db624603706bed400c915c7aeef382956cf8ebbdf3", size = 1138503, upload-time = "2026-03-30T18:57:22.554Z" }, - { url = "https://files.pythonhosted.org/packages/cb/46/a830bcb46f0081630a88cb932c29804553728645c17fd1cff874fe71b1ba/clickhouse_connect-0.15.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:60aa8c9c775d22db324260265f4c656f803fbc71de9193ef83cf8d8d0ef6ab9a", size = 1101890, upload-time = "2026-03-30T18:57:23.788Z" }, - { url = "https://files.pythonhosted.org/packages/4c/05/91cf7cc817ff91bc96f1e2afc84346b42e88831c9c0a7fd56e78907b5320/clickhouse_connect-0.15.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5462bad97d97919a4ed230e2ef28d0b76bec0354a343218647830aac7744a43b", size = 1133723, upload-time = "2026-03-30T18:57:25.105Z" }, - { url = "https://files.pythonhosted.org/packages/d7/b0/e7a71b96b7bc1df6bbacf9fa71f0cc3b8f195f58386535b72aa92304b1fb/clickhouse_connect-0.15.1-cp312-cp312-win32.whl", hash = "sha256:e1a157205efd47884c22bfe061fc6f8c9aea844929ee755c47b446093805d21a", size = 257279, upload-time = "2026-03-30T18:57:26.288Z" }, - { url = "https://files.pythonhosted.org/packages/b9/03/0ef116ef0efc6861d6e9674419709b9873603f330f95853220a145748576/clickhouse_connect-0.15.1-cp312-cp312-win_amd64.whl", hash = "sha256:5de299ada0f7eb9090bb5a6304d8d78163d4d9cc8eb04d8f552bfb82bafb61d5", size = 275916, upload-time = "2026-03-30T18:57:27.372Z" }, - { url = "https://files.pythonhosted.org/packages/dc/94/1c62f55439287999049ddb650fffcc0898ff7639865239d8e414984c7c6b/clickhouse_connect-0.15.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08df7857ecd2e345abbbdfc54d80fa060732cf75c953940355140af9a73b730a", size = 285254, upload-time = "2026-03-30T18:57:28.765Z" }, - { url = "https://files.pythonhosted.org/packages/41/eb/00cf4967be5553b5eca53fbea491a0860816e8f867005d4a3c60b280595c/clickhouse_connect-0.15.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d3fca3e0781b664556690decc788e7d25691043bf67a0d241e9c29233a2990d5", size = 276341, upload-time = "2026-03-30T18:57:29.895Z" }, - { url = "https://files.pythonhosted.org/packages/19/d3/a3c9ce572d3766bfd44626e40ad94539e86516f0a4821417d728e1c01c4e/clickhouse_connect-0.15.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fa01fdb92db6bf72cb9509eecd0a0057a4558a4f40c02eebffbc2d61b644620e", size = 1089774, upload-time = "2026-03-30T18:57:31.005Z" }, - { url = "https://files.pythonhosted.org/packages/6b/45/f10a275a5798cf90fb3a88f64f7410571bab4579cdd443d0820697f0e685/clickhouse_connect-0.15.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c12d9f2b2fc57adaf5ea267804f00e520771794641227ed5285e38fdf36557a6", size = 1110275, upload-time = "2026-03-30T18:57:32.513Z" }, - { url = "https://files.pythonhosted.org/packages/c9/c3/654107d5be702761670eaa58b6882455c428eaf4acc69e0920d87c6d5061/clickhouse_connect-0.15.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a9d1e12bf86cd96626f74d21e3ac237abcda105f55cd2e78d139197d35f86209", size = 1072017, upload-time = "2026-03-30T18:57:33.847Z" }, - { url = "https://files.pythonhosted.org/packages/90/aa/0f3b8f18761d3f8ec7f31a09b0329d19509b7b8f0760a327890ca29f0526/clickhouse_connect-0.15.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b6d107b5f964af97f25a0d1bfd59fe3510f2a646c87ad4f9ab9014bb0c66aa1c", size = 1105152, upload-time = "2026-03-30T18:57:35.204Z" }, - { url = "https://files.pythonhosted.org/packages/28/23/684f6074bf682fd31b1449125d895054af0880bb8481a2902c84e2c9e03b/clickhouse_connect-0.15.1-cp313-cp313-win32.whl", hash = "sha256:46bcebd00aff52ea5f7433e9cee1157b411dba9187f6677a18378c799c27c8aa", size = 256905, upload-time = "2026-03-30T18:57:36.555Z" }, - { url = "https://files.pythonhosted.org/packages/9c/77/39518de3dfb5da2234b2748a133d7626430aa26d7d38bb390c4d8b299cd3/clickhouse_connect-0.15.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f87d283399cbda676c8765605bf60dc6559df6fd38cbb9ea07048a4b34dda26", size = 274792, upload-time = "2026-03-30T18:57:37.79Z" }, - { url = "https://files.pythonhosted.org/packages/31/de/3dda97f102ca428efa958ae8f739b1a5965d3d481ab8f271d2ef99a0c509/clickhouse_connect-0.15.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:8bb70307589099c67dfe9a973998491bc82c1af9040560b5ebab799721bb197d", size = 285678, upload-time = "2026-03-30T18:57:39.303Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d0/650f92e68751093fddcd37d511deea6af27cffe59e70f4a1fcd8da470260/clickhouse_connect-0.15.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:873d8f74eaec141f40ae060318c32353da94fdd4601f925bfd52426f3ddcd689", size = 277362, upload-time = "2026-03-30T18:57:40.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/93/a171190d5985a9dca175f8a5e93ec42f713b1b6f8867208f39598e2dbf05/clickhouse_connect-0.15.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24cdfe9b486c8f2e66f5f51b1f322d89d9eb4df29d9ebb2fa19b553065651e85", size = 1089575, upload-time = "2026-03-30T18:57:42.105Z" }, - { url = "https://files.pythonhosted.org/packages/2f/15/0444a28589ff918a5d5fad8f4484187e571e7cbd0d9290fdb32fee9a28b7/clickhouse_connect-0.15.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b456d469db994b188bb0b5afa373e8f2e5e2bf41a70a736b9ed2485a976e9ae", size = 1101302, upload-time = "2026-03-30T18:57:43.741Z" }, - { url = "https://files.pythonhosted.org/packages/65/88/73d1cfd0156698ca32b49fb0b227aa8500547f902f15df867169677d6a6a/clickhouse_connect-0.15.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:82e60e108d78e32d58a0f21570b02d3baad67ccbad6482eeb79d74a616d8a5ad", size = 1073001, upload-time = "2026-03-30T18:57:45.084Z" }, - { url = "https://files.pythonhosted.org/packages/64/5c/3f2344ec05427d653f1da2e86e06539086fe94c42d73cc979dbeeebe07eb/clickhouse_connect-0.15.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b8236c7dd675ed13d5e96f1f9126eeb711e8c266e4a0476ebc32be8a17decb32", size = 1097680, upload-time = "2026-03-30T18:57:46.484Z" }, - { url = "https://files.pythonhosted.org/packages/fa/6a/f17a367f41d7fca804d16bad850e104f4f441ad67b5c60a07f5889607421/clickhouse_connect-0.15.1-cp314-cp314-win32.whl", hash = "sha256:167e674dff8ac12be7796d93190b6fe9097c042940b3c41d87fe4d85970be27d", size = 260012, upload-time = "2026-03-30T18:57:48.075Z" }, - { url = "https://files.pythonhosted.org/packages/bc/43/778e55b897351881a6794b4b0168b82418675822f78bbd63d29f621b3e63/clickhouse_connect-0.15.1-cp314-cp314-win_amd64.whl", hash = "sha256:a326e2f5518d6a9d71f0895d50a3ccd8c4d5e3abb625f39330512ff3c45c6731", size = 278610, upload-time = "2026-03-30T18:57:49.565Z" }, - { url = "https://files.pythonhosted.org/packages/1d/03/dd2f196cfbcb7f826e93eff7bcec596b7327a9cb395fc5a494a9005d8f69/clickhouse_connect-0.15.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:859c718cb93780dd681f75d59ceaf4415915fa9617a5ba2de6105e291d6df3ad", size = 298561, upload-time = "2026-03-30T18:57:51.189Z" }, - { url = "https://files.pythonhosted.org/packages/13/06/cf0b76e96eb26b4e4797d3ece7f1ba1980aa7767a0b5d53e5b260186b6c4/clickhouse_connect-0.15.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:aa9890507aac52a8a5363813bb315b6867e86a97ffa08576cb934603f5bc0216", size = 293129, upload-time = "2026-03-30T18:57:52.761Z" }, - { url = "https://files.pythonhosted.org/packages/c3/7c/6c9c9f3749e25c0bd6a470c00ade9a76c4fb9aa23c04e48f75a474942d8b/clickhouse_connect-0.15.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aeb09a6f8585f3bd4d8c5bead38f3821c076e0bca08c474a7b9039732a6e2e9a", size = 1170922, upload-time = "2026-03-30T18:57:54.195Z" }, - { url = "https://files.pythonhosted.org/packages/70/78/5507826f580046cd0b0881384539983036efe5db4ccbb2e859e7eb8b0788/clickhouse_connect-0.15.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e88a31bbd9da7f4b49de39d21e8c93c8fbb5cf487071e935af0eba884681df00", size = 1149368, upload-time = "2026-03-30T18:57:55.575Z" }, - { url = "https://files.pythonhosted.org/packages/51/3e/efe0a82a8745ab0f648f1ebb305fef4a654790c22f328f9e17694b0c7979/clickhouse_connect-0.15.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:158325a06978f91a182967341188502a0761447d1e13ffa775cf017def1a3d9e", size = 1131660, upload-time = "2026-03-30T18:57:56.99Z" }, - { url = "https://files.pythonhosted.org/packages/2b/67/f6a2e23ef10d9138733d5e5122ee59f3e5e02c5d8f1a2708f644c4336b8c/clickhouse_connect-0.15.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e702b77720ae6fd501e5a52262518dddb6c705fbc122bca4567694fb0bab401f", size = 1139070, upload-time = "2026-03-30T18:57:58.4Z" }, - { url = "https://files.pythonhosted.org/packages/43/09/d87f6b9377ff19bbe95144917a6683136db77b5957ac0e2f390cbc30313a/clickhouse_connect-0.15.1-cp314-cp314t-win32.whl", hash = "sha256:b2f5174fc6001a1555fc3cb565f3b727e1b786d572df0b30d14929ae13bd3542", size = 282350, upload-time = "2026-03-30T18:58:00.192Z" }, - { url = "https://files.pythonhosted.org/packages/92/cc/8e2437de5e0a6c831e6cb2f282cd33e16bd98213a13624df0cb146a9077e/clickhouse_connect-0.15.1-cp314-cp314t-win_amd64.whl", hash = "sha256:a1266a52bf61f0420630f625c5ac87bc2d095f08321820546300a699d4300ba3", size = 306011, upload-time = "2026-03-30T18:58:02.166Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "contourpy" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" }, - { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" }, - { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" }, - { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" }, - { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" }, - { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" }, - { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" }, - { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" }, - { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" }, - { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" }, - { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, - { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, - { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, - { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, - { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, - { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, - { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, - { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, - { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, - { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, - { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, - { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, - { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, - { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, - { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, - { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, - { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, - { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, - { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, - { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, - { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, - { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, - { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, - { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, - { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, - { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, - { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, - { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, - { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, - { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, - { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, - { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, - { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, - { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, - { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, - { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, - { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, - { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, - { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, - { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, - { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, - { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, - { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, - { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" }, - { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" }, - { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" }, - { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" }, - { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" }, -] - -[[package]] -name = "crcmod" -version = "1.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/b0/e595ce2a2527e169c3bcd6c33d2473c1918e0b7f6826a043ca1245dd4e5b/crcmod-1.7.tar.gz", hash = "sha256:dc7051a0db5f2bd48665a990d3ec1cc305a466a77358ca4492826f41f283601e", size = 89670, upload-time = "2010-06-27T14:35:29.538Z" } - -[[package]] -name = "cryptography" -version = "46.0.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/5d/4a8f770695d73be252331e60e526291e3df0c9b27556a90a6b47bccca4c2/cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4", size = 7179869, upload-time = "2026-04-08T01:56:17.157Z" }, - { url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" }, - { url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" }, - { url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" }, - { url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" }, - { url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" }, - { url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" }, - { url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" }, - { url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" }, - { url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" }, - { url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" }, - { url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" }, - { url = "https://files.pythonhosted.org/packages/1a/bb/a5c213c19ee94b15dfccc48f363738633a493812687f5567addbcbba9f6f/cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457", size = 3026504, upload-time = "2026-04-08T01:56:39.666Z" }, - { url = "https://files.pythonhosted.org/packages/2b/02/7788f9fefa1d060ca68717c3901ae7fffa21ee087a90b7f23c7a603c32ae/cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b", size = 3488363, upload-time = "2026-04-08T01:56:41.893Z" }, - { url = "https://files.pythonhosted.org/packages/7b/56/15619b210e689c5403bb0540e4cb7dbf11a6bf42e483b7644e471a2812b3/cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842", size = 7119671, upload-time = "2026-04-08T01:56:44Z" }, - { url = "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", size = 4268551, upload-time = "2026-04-08T01:56:46.071Z" }, - { url = "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", size = 4408887, upload-time = "2026-04-08T01:56:47.718Z" }, - { url = "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", size = 4271354, upload-time = "2026-04-08T01:56:49.312Z" }, - { url = "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", size = 4905845, upload-time = "2026-04-08T01:56:50.916Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", size = 4444641, upload-time = "2026-04-08T01:56:52.882Z" }, - { url = "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", size = 3967749, upload-time = "2026-04-08T01:56:54.597Z" }, - { url = "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", size = 4270942, upload-time = "2026-04-08T01:56:56.416Z" }, - { url = "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", size = 4871079, upload-time = "2026-04-08T01:56:58.31Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", size = 4443999, upload-time = "2026-04-08T01:57:00.508Z" }, - { url = "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", size = 4399191, upload-time = "2026-04-08T01:57:02.654Z" }, - { url = "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", size = 4655782, upload-time = "2026-04-08T01:57:04.592Z" }, - { url = "https://files.pythonhosted.org/packages/95/b6/3da51d48415bcb63b00dc17c2eff3a651b7c4fed484308d0f19b30e8cb2c/cryptography-46.0.7-cp314-cp314t-win32.whl", hash = "sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298", size = 3002227, upload-time = "2026-04-08T01:57:06.91Z" }, - { url = "https://files.pythonhosted.org/packages/32/a8/9f0e4ed57ec9cebe506e58db11ae472972ecb0c659e4d52bbaee80ca340a/cryptography-46.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb", size = 3475332, upload-time = "2026-04-08T01:57:08.807Z" }, - { url = "https://files.pythonhosted.org/packages/a7/7f/cd42fc3614386bc0c12f0cb3c4ae1fc2bbca5c9662dfed031514911d513d/cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4", size = 7165618, upload-time = "2026-04-08T01:57:10.645Z" }, - { url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" }, - { url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" }, - { url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" }, - { url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" }, - { url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" }, - { url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" }, - { url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" }, - { url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" }, - { url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" }, - { url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" }, - { url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" }, - { url = "https://files.pythonhosted.org/packages/4b/fa/f0ab06238e899cc3fb332623f337a7364f36f4bb3f2534c2bb95a35b132c/cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246", size = 3013001, upload-time = "2026-04-08T01:57:34.933Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", size = 3472985, upload-time = "2026-04-08T01:57:36.714Z" }, - { url = "https://files.pythonhosted.org/packages/63/0c/dca8abb64e7ca4f6b2978769f6fea5ad06686a190cec381f0a796fdcaaba/cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f", size = 3476879, upload-time = "2026-04-08T01:57:38.664Z" }, - { url = "https://files.pythonhosted.org/packages/3a/ea/075aac6a84b7c271578d81a2f9968acb6e273002408729f2ddff517fed4a/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15", size = 4219700, upload-time = "2026-04-08T01:57:40.625Z" }, - { url = "https://files.pythonhosted.org/packages/6c/7b/1c55db7242b5e5612b29fc7a630e91ee7a6e3c8e7bf5406d22e206875fbd/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455", size = 4385982, upload-time = "2026-04-08T01:57:42.725Z" }, - { url = "https://files.pythonhosted.org/packages/cb/da/9870eec4b69c63ef5925bf7d8342b7e13bc2ee3d47791461c4e49ca212f4/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65", size = 4219115, upload-time = "2026-04-08T01:57:44.939Z" }, - { url = "https://files.pythonhosted.org/packages/f4/72/05aa5832b82dd341969e9a734d1812a6aadb088d9eb6f0430fc337cc5a8f/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968", size = 4385479, upload-time = "2026-04-08T01:57:46.86Z" }, - { url = "https://files.pythonhosted.org/packages/20/2a/1b016902351a523aa2bd446b50a5bc1175d7a7d1cf90fe2ef904f9b84ebc/cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4", size = 3412829, upload-time = "2026-04-08T01:57:48.874Z" }, -] - -[[package]] -name = "cycler" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, -] - -[[package]] -name = "deprecation" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788, upload-time = "2020-04-20T14:23:38.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178, upload-time = "2020-04-20T14:23:36.581Z" }, -] - -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, -] - -[[package]] -name = "dnspython" -version = "2.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, -] - -[[package]] -name = "docker" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "requests" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" }, -] - -[[package]] -name = "doris-vector-search" -version = "0.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mysql-connector" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cb/56/b7ba1973dd81192da6ea149bf03e75eca0ff54f80aad749ddcde6679e0ab/doris_vector_search-0.0.9.tar.gz", hash = "sha256:5eb0b8531d1127041a9a0edc874c91841896d6548c8284ac7fe993342e870caf", size = 24794, upload-time = "2025-12-23T05:07:07.743Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/d5/22b8e8f730b32463d648cbc496865ad3ed25c14804c8290b2be330482b7d/doris_vector_search-0.0.9-py3-none-any.whl", hash = "sha256:9f5f22af1d1f66be0550886c6a7187daaf4aba0d2ddf5c58b3602b9de5469e92", size = 27068, upload-time = "2025-12-23T05:07:06.211Z" }, -] - -[[package]] -name = "elastic-transport" -version = "9.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "sniffio" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/23/0a/a92140b666afdcb9862a16e4d80873b3c887c1b7e3f17e945fc3460edf1b/elastic_transport-9.2.1.tar.gz", hash = "sha256:97d9abd638ba8aa90faa4ca1bf1a18bde0fe2088fbc8757f2eb7b299f205773d", size = 77403, upload-time = "2025-12-23T11:54:12.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e6/a42b600ae8b808371f740381f6c32050cad93f870d36cc697b8b7006bf7c/elastic_transport-9.2.1-py3-none-any.whl", hash = "sha256:39e1a25e486af34ce7aa1bc9005d1c736f1b6fb04c9b64ea0604ded5a61fc1d4", size = 65327, upload-time = "2025-12-23T11:54:11.681Z" }, -] - -[[package]] -name = "elasticsearch" -version = "9.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "elastic-transport" }, - { name = "python-dateutil" }, - { name = "sniffio" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0d/15/283459c9299d412ffa2aaab69b082857631c519233f5491d6c567e3320ca/elasticsearch-9.3.0.tar.gz", hash = "sha256:f76e149c0a22d5ccbba58bdc30c9f51cf894231b359ef4fd7e839b558b59f856", size = 893538, upload-time = "2026-02-03T20:26:38.914Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/37/3a196f8918743f2104cb66b1f56218079ecac6e128c061de7df7f4faef02/elasticsearch-9.3.0-py3-none-any.whl", hash = "sha256:67bd2bb4f0800f58c2847d29cd57d6e7bf5bc273483b4f17421f93e75ba09f39", size = 979405, upload-time = "2026-02-03T20:26:34.552Z" }, -] - -[[package]] -name = "endee" -version = "0.1.10" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx", extra = ["http2"] }, - { name = "msgpack" }, - { name = "numpy" }, - { name = "orjson" }, - { name = "pydantic" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/5c/fdbd704bbe867b9a67ca1e296dd8bdd87a6f3474e70b92d32d4e5d7471ac/endee-0.1.10.tar.gz", hash = "sha256:8c24c16622607878beee9abf1fa26fb8419fb058a38d465fe3ea15c8a94f47c9", size = 31163, upload-time = "2026-02-04T05:03:01.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/0f/07cdd543ff8f225a035e3908c388a1246d1fa4fd05211feb5cdb51aedde8/endee-0.1.10-py3-none-any.whl", hash = "sha256:b376a11d0fa368c42160c30502814f8833db5287c61e7c7a6d7edd12987baef6", size = 26751, upload-time = "2026-02-04T05:02:59.642Z" }, -] - -[[package]] -name = "entrypoints" -version = "0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/8d/a7121ffe5f402dc015277d2d31eb82d2187334503a011c18f2e78ecbb9b2/entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4", size = 13974, upload-time = "2022-02-02T21:30:28.172Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/a8/365059bbcd4572cbc41de17fd5b682be5868b218c3c5479071865cab9078/entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f", size = 5294, upload-time = "2022-02-02T21:30:26.024Z" }, -] - -[[package]] -name = "environs" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "marshmallow" }, - { name = "python-dotenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c5/f4/0273d36ab287a00866b69d4008e2ed2e479e4bc1eef4f8be2708f30ad15f/environs-15.0.1.tar.gz", hash = "sha256:638106bfb5d8c9a13ee51c23a9f2416baf9a748c8e537fec771102927cd02aa8", size = 36385, upload-time = "2026-04-06T14:15:12.676Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/fb/11e461eb609792f9f439398eb5f400fb4130ff2b9b59e0ceb346bb830a17/environs-15.0.1-py3-none-any.whl", hash = "sha256:a6aeb3e18d5649a4e74f62984f0d84ec869269cff588a43685272c72b5b8979b", size = 17357, upload-time = "2026-04-06T14:15:11.118Z" }, -] - -[[package]] -name = "events" -version = "0.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/ed/e47dec0626edd468c84c04d97769e7ab4ea6457b7f54dcb3f72b17fcd876/Events-0.5-py3-none-any.whl", hash = "sha256:a7286af378ba3e46640ac9825156c93bdba7502174dd696090fdfcd4d80a1abd", size = 6758, upload-time = "2023-07-31T08:23:13.645Z" }, -] - -[[package]] -name = "faker" -version = "40.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tzdata", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/89/95/4822ffe94723553789aef783104f4f18fc20d7c4c68e1bbd633e11d09758/faker-40.13.0.tar.gz", hash = "sha256:a0751c84c3abac17327d7bb4c98e8afe70ebf7821e01dd7d0b15cd8856415525", size = 1962043, upload-time = "2026-04-06T16:44:55.68Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/8a/708103325edff16a0b0e004de0d37db8ba216a32713948c64d71f6d4a4c2/faker-40.13.0-py3-none-any.whl", hash = "sha256:c1298fd0d819b3688fb5fd358c4ba8f56c7c8c740b411fd3dbd8e30bf2c05019", size = 1994597, upload-time = "2026-04-06T16:44:53.698Z" }, -] - -[[package]] -name = "fastapi" -version = "0.125.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "pydantic" }, - { name = "starlette" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/17/71/2df15009fb4bdd522a069d2fbca6007c6c5487fce5cb965be00fc335f1d1/fastapi-0.125.0.tar.gz", hash = "sha256:16b532691a33e2c5dee1dac32feb31dc6eb41a3dd4ff29a95f9487cb21c054c0", size = 370550, upload-time = "2025-12-17T21:41:44.15Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/2f/ff2fcc98f500713368d8b650e1bbc4a0b3ebcdd3e050dcdaad5f5a13fd7e/fastapi-0.125.0-py3-none-any.whl", hash = "sha256:2570ec4f3aecf5cca8f0428aed2398b774fcdfee6c2116f86e80513f2f86a7a1", size = 112888, upload-time = "2025-12-17T21:41:41.286Z" }, -] - -[[package]] -name = "fastcore" -version = "1.12.35" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/cc/8837b60a7fa568354af17f0cb1e3f9b8ff8c0c6633a34f4629d925a153cf/fastcore-1.12.35.tar.gz", hash = "sha256:f7ea8526315aa76910833031a2917bd900db8245ec50be592db37d8567417282", size = 96087, upload-time = "2026-04-10T00:52:52.411Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/58/cb89f6f39ad54192b02e7df579172445880b4d19448a835e499b8cc5a002/fastcore-1.12.35-py3-none-any.whl", hash = "sha256:76ceb76754469e9fad44ab83e5e995b70119baf7703caaee5ff6f88be708c97b", size = 100737, upload-time = "2026-04-10T00:52:50.839Z" }, -] - -[[package]] -name = "favicon" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beautifulsoup4" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/64/68/d2646f40c05d3a501cddd232119f8c087a6fcba3c79255a062c73e80b42a/favicon-0.7.0.tar.gz", hash = "sha256:6d6b5a78de2a0d0084589f687f384b2ecd6a6527093fec564403b1a30605d7a8", size = 9284, upload-time = "2019-08-31T16:56:42.464Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/4c/8baf94bb789972634d933152d27529f2bad4e5d2397b8da9c30f6f5342ce/favicon-0.7.0-py2.py3-none-any.whl", hash = "sha256:7fec0617c73dcb8521ea788e1d38cdc7226c7cb8e28c81e11625d85fa1534880", size = 5921, upload-time = "2019-08-31T16:56:40.751Z" }, -] - -[[package]] -name = "filelock" -version = "3.25.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, -] - -[[package]] -name = "flask" -version = "3.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "blinker" }, - { name = "click" }, - { name = "itsdangerous" }, - { name = "jinja2" }, - { name = "markupsafe" }, - { name = "werkzeug" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, -] - -[[package]] -name = "flatbuffers" -version = "25.12.19" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, -] - -[[package]] -name = "fonttools" -version = "4.62.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/08/7012b00a9a5874311b639c3920270c36ee0c445b69d9989a85e5c92ebcb0/fonttools-4.62.1.tar.gz", hash = "sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d", size = 3580737, upload-time = "2026-03-13T13:54:25.52Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/23ff32561ec8d45a4d48578b4d241369d9270dc50926c017570e60893701/fonttools-4.62.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7", size = 2871039, upload-time = "2026-03-13T13:52:33.127Z" }, - { url = "https://files.pythonhosted.org/packages/24/7f/66d3f8a9338a9b67fe6e1739f47e1cd5cee78bd3bc1206ef9b0b982289a5/fonttools-4.62.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14", size = 2416346, upload-time = "2026-03-13T13:52:35.676Z" }, - { url = "https://files.pythonhosted.org/packages/aa/53/5276ceba7bff95da7793a07c5284e1da901cf00341ce5e2f3273056c0cca/fonttools-4.62.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6acb4109f8bee00fec985c8c7afb02299e35e9c94b57287f3ea542f28bd0b0a7", size = 5100897, upload-time = "2026-03-13T13:52:38.102Z" }, - { url = "https://files.pythonhosted.org/packages/cc/a1/40a5c4d8e28b0851d53a8eeeb46fbd73c325a2a9a165f290a5ed90e6c597/fonttools-4.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b", size = 5071078, upload-time = "2026-03-13T13:52:41.305Z" }, - { url = "https://files.pythonhosted.org/packages/e3/be/d378fca4c65ea1956fee6d90ace6e861776809cbbc5af22388a090c3c092/fonttools-4.62.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a5d8825e1140f04e6c99bb7d37a9e31c172f3bc208afbe02175339e699c710e1", size = 5076908, upload-time = "2026-03-13T13:52:44.122Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d9/ae6a1d0693a4185a84605679c8a1f719a55df87b9c6e8e817bfdd9ef5936/fonttools-4.62.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:268abb1cb221e66c014acc234e872b7870d8b5d4657a83a8f4205094c32d2416", size = 5202275, upload-time = "2026-03-13T13:52:46.591Z" }, - { url = "https://files.pythonhosted.org/packages/54/6c/af95d9c4efb15cabff22642b608342f2bd67137eea6107202d91b5b03184/fonttools-4.62.1-cp311-cp311-win32.whl", hash = "sha256:942b03094d7edbb99bdf1ae7e9090898cad7bf9030b3d21f33d7072dbcb51a53", size = 2293075, upload-time = "2026-03-13T13:52:48.711Z" }, - { url = "https://files.pythonhosted.org/packages/d3/97/bf54c5b3f2be34e1f143e6db838dfdc54f2ffa3e68c738934c82f3b2a08d/fonttools-4.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2", size = 2344593, upload-time = "2026-03-13T13:52:50.725Z" }, - { url = "https://files.pythonhosted.org/packages/47/d4/dbacced3953544b9a93088cc10ef2b596d348c983d5c67a404fa41ec51ba/fonttools-4.62.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:90365821debbd7db678809c7491ca4acd1e0779b9624cdc6ddaf1f31992bf974", size = 2870219, upload-time = "2026-03-13T13:52:53.664Z" }, - { url = "https://files.pythonhosted.org/packages/66/9e/a769c8e99b81e5a87ab7e5e7236684de4e96246aae17274e5347d11ebd78/fonttools-4.62.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12859ff0b47dd20f110804c3e0d0970f7b832f561630cd879969011541a464a9", size = 2414891, upload-time = "2026-03-13T13:52:56.493Z" }, - { url = "https://files.pythonhosted.org/packages/69/64/f19a9e3911968c37e1e620e14dfc5778299e1474f72f4e57c5ec771d9489/fonttools-4.62.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c125ffa00c3d9003cdaaf7f2c79e6e535628093e14b5de1dccb08859b680936", size = 5033197, upload-time = "2026-03-13T13:52:59.179Z" }, - { url = "https://files.pythonhosted.org/packages/9b/8a/99c8b3c3888c5c474c08dbfd7c8899786de9604b727fcefb055b42c84bba/fonttools-4.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:149f7d84afca659d1a97e39a4778794a2f83bf344c5ee5134e09995086cc2392", size = 4988768, upload-time = "2026-03-13T13:53:02.761Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c6/0f904540d3e6ab463c1243a0d803504826a11604c72dd58c2949796a1762/fonttools-4.62.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0aa72c43a601cfa9273bb1ae0518f1acadc01ee181a6fc60cd758d7fdadffc04", size = 4971512, upload-time = "2026-03-13T13:53:05.678Z" }, - { url = "https://files.pythonhosted.org/packages/29/0b/5cbef6588dc9bd6b5c9ad6a4d5a8ca384d0cea089da31711bbeb4f9654a6/fonttools-4.62.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:19177c8d96c7c36359266e571c5173bcee9157b59cfc8cb0153c5673dc5a3a7d", size = 5122723, upload-time = "2026-03-13T13:53:08.662Z" }, - { url = "https://files.pythonhosted.org/packages/4a/47/b3a5342d381595ef439adec67848bed561ab7fdb1019fa522e82101b7d9c/fonttools-4.62.1-cp312-cp312-win32.whl", hash = "sha256:a24decd24d60744ee8b4679d38e88b8303d86772053afc29b19d23bb8207803c", size = 2281278, upload-time = "2026-03-13T13:53:10.998Z" }, - { url = "https://files.pythonhosted.org/packages/28/b1/0c2ab56a16f409c6c8a68816e6af707827ad5d629634691ff60a52879792/fonttools-4.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e7863e10b3de72376280b515d35b14f5eeed639d1aa7824f4cf06779ec65e42", size = 2331414, upload-time = "2026-03-13T13:53:13.992Z" }, - { url = "https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79", size = 2865155, upload-time = "2026-03-13T13:53:16.132Z" }, - { url = "https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe", size = 2412802, upload-time = "2026-03-13T13:53:18.878Z" }, - { url = "https://files.pythonhosted.org/packages/52/94/e6ac4b44026de7786fe46e3bfa0c87e51d5d70a841054065d49cd62bb909/fonttools-4.62.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68", size = 5013926, upload-time = "2026-03-13T13:53:21.379Z" }, - { url = "https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1", size = 4964575, upload-time = "2026-03-13T13:53:23.857Z" }, - { url = "https://files.pythonhosted.org/packages/46/76/7d051671e938b1881670528fec69cc4044315edd71a229c7fd712eaa5119/fonttools-4.62.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069", size = 4953693, upload-time = "2026-03-13T13:53:26.569Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ae/b41f8628ec0be3c1b934fc12b84f4576a5c646119db4d3bdd76a217c90b5/fonttools-4.62.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9", size = 5094920, upload-time = "2026-03-13T13:53:29.329Z" }, - { url = "https://files.pythonhosted.org/packages/f2/f6/53a1e9469331a23dcc400970a27a4caa3d9f6edbf5baab0260285238b884/fonttools-4.62.1-cp313-cp313-win32.whl", hash = "sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24", size = 2279928, upload-time = "2026-03-13T13:53:32.352Z" }, - { url = "https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056", size = 2330514, upload-time = "2026-03-13T13:53:34.991Z" }, - { url = "https://files.pythonhosted.org/packages/36/f0/2888cdac391807d68d90dcb16ef858ddc1b5309bfc6966195a459dd326e2/fonttools-4.62.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca", size = 2864442, upload-time = "2026-03-13T13:53:37.509Z" }, - { url = "https://files.pythonhosted.org/packages/4b/b2/e521803081f8dc35990816b82da6360fa668a21b44da4b53fc9e77efcd62/fonttools-4.62.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca", size = 2410901, upload-time = "2026-03-13T13:53:40.55Z" }, - { url = "https://files.pythonhosted.org/packages/00/a4/8c3511ff06e53110039358dbbdc1a65d72157a054638387aa2ada300a8b8/fonttools-4.62.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782", size = 4999608, upload-time = "2026-03-13T13:53:42.798Z" }, - { url = "https://files.pythonhosted.org/packages/28/63/cd0c3b26afe60995a5295f37c246a93d454023726c3261cfbb3559969bb9/fonttools-4.62.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae", size = 4912726, upload-time = "2026-03-13T13:53:45.405Z" }, - { url = "https://files.pythonhosted.org/packages/70/b9/ac677cb07c24c685cf34f64e140617d58789d67a3dd524164b63648c6114/fonttools-4.62.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7", size = 4951422, upload-time = "2026-03-13T13:53:48.326Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/11c08419a14b85b7ca9a9faca321accccc8842dd9e0b1c8a72908de05945/fonttools-4.62.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a", size = 5060979, upload-time = "2026-03-13T13:53:51.366Z" }, - { url = "https://files.pythonhosted.org/packages/4e/3c/12eea4a4cf054e7ab058ed5ceada43b46809fce2bf319017c4d63ae55bb4/fonttools-4.62.1-cp314-cp314-win32.whl", hash = "sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800", size = 2283733, upload-time = "2026-03-13T13:53:53.606Z" }, - { url = "https://files.pythonhosted.org/packages/6b/67/74b070029043186b5dd13462c958cb7c7f811be0d2e634309d9a1ffb1505/fonttools-4.62.1-cp314-cp314-win_amd64.whl", hash = "sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e", size = 2335663, upload-time = "2026-03-13T13:53:56.23Z" }, - { url = "https://files.pythonhosted.org/packages/42/c5/4d2ed3ca6e33617fc5624467da353337f06e7f637707478903c785bd8e20/fonttools-4.62.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82", size = 2947288, upload-time = "2026-03-13T13:53:59.397Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e9/7ab11ddfda48ed0f89b13380e5595ba572619c27077be0b2c447a63ff351/fonttools-4.62.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260", size = 2449023, upload-time = "2026-03-13T13:54:01.642Z" }, - { url = "https://files.pythonhosted.org/packages/b2/10/a800fa090b5e8819942e54e19b55fc7c21fe14a08757c3aa3ca8db358939/fonttools-4.62.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4", size = 5137599, upload-time = "2026-03-13T13:54:04.495Z" }, - { url = "https://files.pythonhosted.org/packages/37/dc/8ccd45033fffd74deb6912fa1ca524643f584b94c87a16036855b498a1ed/fonttools-4.62.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b", size = 4920933, upload-time = "2026-03-13T13:54:07.557Z" }, - { url = "https://files.pythonhosted.org/packages/99/eb/e618adefb839598d25ac8136cd577925d6c513dc0d931d93b8af956210f0/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87", size = 5016232, upload-time = "2026-03-13T13:54:10.611Z" }, - { url = "https://files.pythonhosted.org/packages/d9/5f/9b5c9bfaa8ec82def8d8168c4f13615990d6ce5996fe52bd49bfb5e05134/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c", size = 5042987, upload-time = "2026-03-13T13:54:13.569Z" }, - { url = "https://files.pythonhosted.org/packages/90/aa/dfbbe24c6a6afc5c203d90cc0343e24bcbb09e76d67c4d6eef8c2558d7ba/fonttools-4.62.1-cp314-cp314t-win32.whl", hash = "sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a", size = 2348021, upload-time = "2026-03-13T13:54:16.98Z" }, - { url = "https://files.pythonhosted.org/packages/13/6f/ae9c4e4dd417948407b680855c2c7790efb52add6009aaecff1e3bc50e8e/fonttools-4.62.1-cp314-cp314t-win_amd64.whl", hash = "sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e", size = 2414147, upload-time = "2026-03-13T13:54:19.416Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd", size = 1152647, upload-time = "2026-03-13T13:54:22.735Z" }, -] - -[[package]] -name = "frozenlist" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, - { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, - { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, - { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, - { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, - { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, - { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, - { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, - { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, - { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, - { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, - { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, - { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, - { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, - { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, - { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, - { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, - { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, - { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, - { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, - { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, - { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, - { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, - { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, - { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, - { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, - { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, - { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, - { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, - { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, - { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, - { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, - { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, - { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, - { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, - { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, - { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, - { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, - { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, - { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, - { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, - { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, - { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, - { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, - { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, - { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, - { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, - { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, - { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, - { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, - { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, - { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, - { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, - { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, - { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, - { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, - { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, - { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, - { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, - { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, - { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, - { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, - { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, - { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, -] - -[[package]] -name = "fsspec" -version = "2026.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e1/cf/b50ddf667c15276a9ab15a70ef5f257564de271957933ffea49d2cdbcdfb/fsspec-2026.3.0.tar.gz", hash = "sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41", size = 313547, upload-time = "2026-03-27T19:11:14.892Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl", hash = "sha256:d2ceafaad1b3457968ed14efa28798162f1638dbb5d2a6868a2db002a5ee39a4", size = 202595, upload-time = "2026-03-27T19:11:13.595Z" }, -] - -[[package]] -name = "gitdb" -version = "4.0.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "smmap" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, -] - -[[package]] -name = "gitpython" -version = "3.1.46" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "gitdb" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, -] - -[[package]] -name = "greenlet" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/86/94/a5935717b307d7c71fe877b52b884c6af707d2d2090db118a03fbd799369/greenlet-3.4.0.tar.gz", hash = "sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff", size = 195913, upload-time = "2026-04-08T17:08:00.863Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/c6/dba32cab7e3a625b011aa5647486e2d28423a48845a2998c126dd69c85e1/greenlet-3.4.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:805bebb4945094acbab757d34d6e1098be6de8966009ab9ca54f06ff492def58", size = 285504, upload-time = "2026-04-08T15:52:14.071Z" }, - { url = "https://files.pythonhosted.org/packages/54/f4/7cb5c2b1feb9a1f50e038be79980dfa969aa91979e5e3a18fdbcfad2c517/greenlet-3.4.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:439fc2f12b9b512d9dfa681c5afe5f6b3232c708d13e6f02c845e0d9f4c2d8c6", size = 605476, upload-time = "2026-04-08T16:24:37.064Z" }, - { url = "https://files.pythonhosted.org/packages/d6/af/b66ab0b2f9a4c5a867c136bf66d9599f34f21a1bcca26a2884a29c450bd9/greenlet-3.4.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a70ed1cb0295bee1df57b63bf7f46b4e56a5c93709eea769c1fec1bb23a95875", size = 618336, upload-time = "2026-04-08T16:30:56.59Z" }, - { url = "https://files.pythonhosted.org/packages/e5/5c/8c5633ece6ba611d64bf2770219a98dd439921d6424e4e8cf16b0ac74ea5/greenlet-3.4.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c660bce1940a1acae5f51f0a064f1bc785d07ea16efcb4bc708090afc4d69e83", size = 613515, upload-time = "2026-04-08T15:56:32.478Z" }, - { url = "https://files.pythonhosted.org/packages/a9/df/950d15bca0d90a0e7395eb777903060504cdb509b7b705631e8fb69ff415/greenlet-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ee407d4d1ca9dc632265aee1c8732c4a2d60adff848057cdebfe5fe94eb2c8a2", size = 1574623, upload-time = "2026-04-08T16:26:18.596Z" }, - { url = "https://files.pythonhosted.org/packages/1a/e7/0839afab829fcb7333c9ff6d80c040949510055d2d4d63251f0d1c7c804e/greenlet-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:956215d5e355fffa7c021d168728321fd4d31fd730ac609b1653b450f6a4bc71", size = 1639579, upload-time = "2026-04-08T15:57:29.231Z" }, - { url = "https://files.pythonhosted.org/packages/d9/2b/b4482401e9bcaf9f5c97f67ead38db89c19520ff6d0d6699979c6efcc200/greenlet-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:5cb614ace7c27571270354e9c9f696554d073f8aa9319079dcba466bbdead711", size = 238233, upload-time = "2026-04-08T17:02:54.286Z" }, - { url = "https://files.pythonhosted.org/packages/0c/4d/d8123a4e0bcd583d5cfc8ddae0bbe29c67aab96711be331a7cc935a35966/greenlet-3.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:04403ac74fe295a361f650818de93be11b5038a78f49ccfb64d3b1be8fbf1267", size = 235045, upload-time = "2026-04-08T17:04:05.072Z" }, - { url = "https://files.pythonhosted.org/packages/65/8b/3669ad3b3f247a791b2b4aceb3aa5a31f5f6817bf547e4e1ff712338145a/greenlet-3.4.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a", size = 286902, upload-time = "2026-04-08T15:52:12.138Z" }, - { url = "https://files.pythonhosted.org/packages/38/3e/3c0e19b82900873e2d8469b590a6c4b3dfd2b316d0591f1c26b38a4879a5/greenlet-3.4.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97", size = 606099, upload-time = "2026-04-08T16:24:38.408Z" }, - { url = "https://files.pythonhosted.org/packages/b5/33/99fef65e7754fc76a4ed14794074c38c9ed3394a5bd129d7f61b705f3168/greenlet-3.4.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996", size = 618837, upload-time = "2026-04-08T16:30:58.298Z" }, - { url = "https://files.pythonhosted.org/packages/36/f7/229f3aed6948faa20e0616a0b8568da22e365ede6a54d7d369058b128afd/greenlet-3.4.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc", size = 615062, upload-time = "2026-04-08T15:56:33.766Z" }, - { url = "https://files.pythonhosted.org/packages/08/97/d988180011aa40135c46cd0d0cf01dd97f7162bae14139b4a3ef54889ba5/greenlet-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de", size = 1573511, upload-time = "2026-04-08T16:26:20.058Z" }, - { url = "https://files.pythonhosted.org/packages/d4/0f/a5a26fe152fb3d12e6a474181f6e9848283504d0afd095f353d85726374b/greenlet-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08", size = 1640396, upload-time = "2026-04-08T15:57:30.88Z" }, - { url = "https://files.pythonhosted.org/packages/42/cf/bb2c32d9a100e36ee9f6e38fad6b1e082b8184010cb06259b49e1266ca01/greenlet-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2", size = 238892, upload-time = "2026-04-08T17:03:10.094Z" }, - { url = "https://files.pythonhosted.org/packages/b7/47/6c41314bac56e71436ce551c7fbe3cc830ed857e6aa9708dbb9c65142eb6/greenlet-3.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e", size = 235599, upload-time = "2026-04-08T15:52:54.3Z" }, - { url = "https://files.pythonhosted.org/packages/7a/75/7e9cd1126a1e1f0cd67b0eda02e5221b28488d352684704a78ed505bd719/greenlet-3.4.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1", size = 285856, upload-time = "2026-04-08T15:52:45.82Z" }, - { url = "https://files.pythonhosted.org/packages/9d/c4/3e2df392e5cb199527c4d9dbcaa75c14edcc394b45040f0189f649631e3c/greenlet-3.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1", size = 610208, upload-time = "2026-04-08T16:24:39.674Z" }, - { url = "https://files.pythonhosted.org/packages/da/af/750cdfda1d1bd30a6c28080245be8d0346e669a98fdbae7f4102aa95fff3/greenlet-3.4.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82", size = 621269, upload-time = "2026-04-08T16:30:59.767Z" }, - { url = "https://files.pythonhosted.org/packages/54/78/0cbc693622cd54ebe25207efbb3a0eb07c2639cb8594f6e3aaaa0bb077a8/greenlet-3.4.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf", size = 617549, upload-time = "2026-04-08T15:56:34.893Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c0/8966767de01343c1ff47e8b855dc78e7d1a8ed2b7b9c83576a57e289f81d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729", size = 1575310, upload-time = "2026-04-08T16:26:21.671Z" }, - { url = "https://files.pythonhosted.org/packages/b8/38/bcdc71ba05e9a5fda87f63ffc2abcd1f15693b659346df994a48c968003d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c", size = 1640435, upload-time = "2026-04-08T15:57:32.572Z" }, - { url = "https://files.pythonhosted.org/packages/a1/c2/19b664b7173b9e4ef5f77e8cef9f14c20ec7fce7920dc1ccd7afd955d093/greenlet-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940", size = 238760, upload-time = "2026-04-08T17:04:03.878Z" }, - { url = "https://files.pythonhosted.org/packages/9b/96/795619651d39c7fbd809a522f881aa6f0ead504cc8201c3a5b789dfaef99/greenlet-3.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a", size = 235498, upload-time = "2026-04-08T17:05:00.584Z" }, - { url = "https://files.pythonhosted.org/packages/78/02/bde66806e8f169cf90b14d02c500c44cdbe02c8e224c9c67bafd1b8cadd1/greenlet-3.4.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e", size = 286291, upload-time = "2026-04-08T17:09:34.307Z" }, - { url = "https://files.pythonhosted.org/packages/05/1f/39da1c336a87d47c58352fb8a78541ce63d63ae57c5b9dae1fe02801bbc2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d", size = 656749, upload-time = "2026-04-08T16:24:41.721Z" }, - { url = "https://files.pythonhosted.org/packages/d3/6c/90ee29a4ee27af7aa2e2ec408799eeb69ee3fcc5abcecac6ddd07a5cd0f2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615", size = 669084, upload-time = "2026-04-08T16:31:01.372Z" }, - { url = "https://files.pythonhosted.org/packages/07/49/d4cad6e5381a50947bb973d2f6cf6592621451b09368b8c20d9b8af49c5b/greenlet-3.4.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf", size = 665621, upload-time = "2026-04-08T15:56:35.995Z" }, - { url = "https://files.pythonhosted.org/packages/37/31/d1edd54f424761b5d47718822f506b435b6aab2f3f93b465441143ea5119/greenlet-3.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf", size = 1622259, upload-time = "2026-04-08T16:26:23.201Z" }, - { url = "https://files.pythonhosted.org/packages/b0/c6/6d3f9cdcb21c4e12a79cb332579f1c6aa1af78eb68059c5a957c7812d95e/greenlet-3.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda", size = 1686916, upload-time = "2026-04-08T15:57:34.282Z" }, - { url = "https://files.pythonhosted.org/packages/63/45/c1ca4a1ad975de4727e52d3ffe641ae23e1d7a8ffaa8ff7a0477e1827b92/greenlet-3.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d", size = 239821, upload-time = "2026-04-08T17:03:48.423Z" }, - { url = "https://files.pythonhosted.org/packages/71/c4/6f621023364d7e85a4769c014c8982f98053246d142420e0328980933ceb/greenlet-3.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:f8296d4e2b92af34ebde81085a01690f26a51eb9ac09a0fcadb331eb36dbc802", size = 236932, upload-time = "2026-04-08T17:04:33.551Z" }, - { url = "https://files.pythonhosted.org/packages/d4/8f/18d72b629783f5e8d045a76f5325c1e938e659a9e4da79c7dcd10169a48d/greenlet-3.4.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece", size = 294681, upload-time = "2026-04-08T15:52:35.778Z" }, - { url = "https://files.pythonhosted.org/packages/9e/ad/5fa86ec46769c4153820d58a04062285b3b9e10ba3d461ee257b68dcbf53/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8", size = 658899, upload-time = "2026-04-08T16:24:43.32Z" }, - { url = "https://files.pythonhosted.org/packages/43/f0/4e8174ca0e87ae748c409f055a1ba161038c43cc0a5a6f1433a26ac2e5bf/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2", size = 665284, upload-time = "2026-04-08T16:31:02.833Z" }, - { url = "https://files.pythonhosted.org/packages/19/da/991cf7cd33662e2df92a1274b7eb4d61769294d38a1bba8a45f31364845e/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed", size = 661861, upload-time = "2026-04-08T15:56:37.269Z" }, - { url = "https://files.pythonhosted.org/packages/36/c5/6c2c708e14db3d9caea4b459d8464f58c32047451142fe2cfd90e7458f41/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f", size = 1622182, upload-time = "2026-04-08T16:26:24.777Z" }, - { url = "https://files.pythonhosted.org/packages/7a/4c/50c5fed19378e11a29fabab1f6be39ea95358f4a0a07e115a51ca93385d8/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a", size = 1685050, upload-time = "2026-04-08T15:57:36.453Z" }, - { url = "https://files.pythonhosted.org/packages/db/72/85ae954d734703ab48e622c59d4ce35d77ce840c265814af9c078cacc7aa/greenlet-3.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705", size = 245554, upload-time = "2026-04-08T17:03:50.044Z" }, -] - -[[package]] -name = "grpcio" -version = "1.53.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/13/f7e3834ba272c0058c66de57272fb3b20dde69003032fdb6b4d7c36eebed/grpcio-1.53.0.tar.gz", hash = "sha256:a4952899b4931a6ba12951f9a141ef3e74ff8a6ec9aa2dc602afa40f63595e33", size = 22407809, upload-time = "2023-03-27T19:22:37.084Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/17/6cb16b21be317dcf621961d9ed042c4ef463263552d1ab5048cc032e9feb/grpcio-1.53.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:1b172e6d497191940c4b8d75b53de82dc252e15b61de2951d577ec5b43316b29", size = 88512765, upload-time = "2023-03-27T19:17:36.132Z" }, - { url = "https://files.pythonhosted.org/packages/36/8b/a896e68d36be5e778cc6a4ba47d23667e92281d669cedf3f7f1632e30cf4/grpcio-1.53.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:82434ba3a5935e47908bc861ce1ebc43c2edfc1001d235d6e31e5d3ed55815f7", size = 8452469, upload-time = "2023-03-27T19:17:41.27Z" }, - { url = "https://files.pythonhosted.org/packages/c3/57/015e76827100e0be9047e78efa7fba6f443c3c5e22b57245757da0f6ed5b/grpcio-1.53.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:1c734a2d4843e4e14ececf5600c3c4750990ec319e1299db7e4f0d02c25c1467", size = 4322107, upload-time = "2023-03-27T19:17:46.436Z" }, - { url = "https://files.pythonhosted.org/packages/fa/7c/961adcfdfc945cd4155ae66d6e6bb6c2e68a4fef37f3753af89f8c4b5f06/grpcio-1.53.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a2ead3de3b2d53119d473aa2f224030257ef33af1e4ddabd4afee1dea5f04c", size = 5222262, upload-time = "2023-03-27T19:17:50.606Z" }, - { url = "https://files.pythonhosted.org/packages/8c/7a/85586e639dab8d9ffbb5787843ad3b05318b60122481481614da47a535b9/grpcio-1.53.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a34d6e905f071f9b945cabbcc776e2055de1fdb59cd13683d9aa0a8f265b5bf9", size = 4949139, upload-time = "2023-03-27T19:17:54.089Z" }, - { url = "https://files.pythonhosted.org/packages/a7/86/b7b1cc9b557152f913868ff7f3688bc698820cedc59d47347438aa8dbbfe/grpcio-1.53.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eaf8e3b97caaf9415227a3c6ca5aa8d800fecadd526538d2bf8f11af783f1550", size = 5451623, upload-time = "2023-03-27T19:17:57.515Z" }, - { url = "https://files.pythonhosted.org/packages/44/2a/d4914473086d6343c7480a659adb70157dc025f1b7405728299c8968539f/grpcio-1.53.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:da95778d37be8e4e9afca771a83424f892296f5dfb2a100eda2571a1d8bbc0dc", size = 5175649, upload-time = "2023-03-27T19:18:00.932Z" }, - { url = "https://files.pythonhosted.org/packages/6b/b9/96024a9fc4baed757064d79884e15cd6df65f39cf990095d0da5e6c73211/grpcio-1.53.0-cp311-cp311-win32.whl", hash = "sha256:e4f513d63df6336fd84b74b701f17d1bb3b64e9d78a6ed5b5e8a198bbbe8bbfa", size = 3302728, upload-time = "2023-03-27T19:18:03.598Z" }, - { url = "https://files.pythonhosted.org/packages/82/2f/5371b77dab25615b93ec95fd49b63a60fb5c2a467b6c281836719669045a/grpcio-1.53.0-cp311-cp311-win_amd64.whl", hash = "sha256:ddb2511fbbb440ed9e5c9a4b9b870f2ed649b7715859fd6f2ebc585ee85c0364", size = 3941501, upload-time = "2023-03-27T19:18:06.479Z" }, -] - -[[package]] -name = "grpcio-tools" -version = "1.53.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "grpcio" }, - { name = "protobuf" }, - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/81/dc/71fb99bc79deb2cbf2007337b946e463d8a1996d15177db245164f389bc9/grpcio-tools-1.53.0.tar.gz", hash = "sha256:925efff2d63ca3266f93c924ffeba5d496f16a8ccbe125fa0d18acf47cc5fa88", size = 2257165, upload-time = "2023-03-27T19:22:56.224Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/d2/3f657e6171b676c69ab3d102c900a96d709257b061caad5ce8c45b2444d6/grpcio_tools-1.53.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:613a84ebd1881635370c12503f2b15b37332a53fbac32904c94ac4c0c10f0a2a", size = 37001527, upload-time = "2023-03-27T19:20:45.905Z" }, - { url = "https://files.pythonhosted.org/packages/6d/6d/9fb506fd0e3f8ddc6e93db7b5e2e12877f33a073e970acdabff5bfdf1325/grpcio_tools-1.53.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:af686b83bc6b5c1f1591c9f49183717974047de9546adcf5e09a18781b550c96", size = 3881085, upload-time = "2023-03-27T19:20:50.152Z" }, - { url = "https://files.pythonhosted.org/packages/b5/23/c3cddfa60a6b2b822d29b26fb1ac2ce9907a5c04a1aad9d55ad260a94f94/grpcio_tools-1.53.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:3cc832e8297e9437bc2b137fe815c8ba1d9af6ffdd76c5c6d7f911bf8e1b0f45", size = 2043803, upload-time = "2023-03-27T19:20:53.085Z" }, - { url = "https://files.pythonhosted.org/packages/9b/4a/9ba61ee47d2835635c0b0f8ab1e79f7f09070c3f88646ef988c70094b9c9/grpcio_tools-1.53.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39d0a254de49d852f5fe9f9df0a45b2ae66bc04e2d9ee1d6d2c0ba1e70fac91a", size = 2521638, upload-time = "2023-03-27T19:20:55.312Z" }, - { url = "https://files.pythonhosted.org/packages/33/13/be80c333923f77a6636bdebb6cf376a9ba0e141e177f595249e364e8a97c/grpcio_tools-1.53.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7062109553ec1873c5c09cc379b8ae0aa76a2d6d6aae97759b97787b93fa9786", size = 2375474, upload-time = "2023-03-27T19:20:58.024Z" }, - { url = "https://files.pythonhosted.org/packages/9b/19/93917155fae555ff383e81762ec3ff48098e373b9a2fe8205a342c02e538/grpcio_tools-1.53.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7728407b1e89fb1473b86152fc33be00f1a25a5aa3264245521f05cbbef9d817", size = 2970898, upload-time = "2023-03-27T19:21:01.057Z" }, - { url = "https://files.pythonhosted.org/packages/fa/9f/c701728fb82085dd3ae1ec7fb5eae2198fa518962b6f87f27d3cd7e38be0/grpcio_tools-1.53.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2758ea125442bc81251267fc9c28f65555a571f6a0afda4d71a6e7d669347095", size = 2733921, upload-time = "2023-03-27T19:21:04.027Z" }, - { url = "https://files.pythonhosted.org/packages/ee/31/8685796e36b68ccb45190f46af16e3ef73b7fd26e0a080880d0cd1c5b7d3/grpcio_tools-1.53.0-cp311-cp311-win32.whl", hash = "sha256:8940d59fca790f1bd45785d0661c3a8c081231c9f8049d7fbf6c6c00737e43da", size = 1425396, upload-time = "2023-03-27T19:21:06.695Z" }, - { url = "https://files.pythonhosted.org/packages/d8/1c/4d0243d007a95e6cfae4e2db1ff507302f756b80d7cd2db24b2e343ac26f/grpcio_tools-1.53.0-cp311-cp311-win_amd64.whl", hash = "sha256:c2cff79be5a06d63e9a6a7e38f8f160ade21517386eabe27afacef65a8531358", size = 1692622, upload-time = "2023-03-27T19:21:09.414Z" }, -] - -[[package]] -name = "h11" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, -] - -[[package]] -name = "h2" -version = "4.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hpack" }, - { name = "hyperframe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" }, -] - -[[package]] -name = "hdrhistogram" -version = "0.10.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pbr" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c2/79/674aad5279dd1a77b85efa1cbf8dcead209dc5f38f55cbbfd75bc20cc65b/hdrhistogram-0.10.3.tar.gz", hash = "sha256:f3890df0a6f3c582a0a8b2a49a568729cb319f1600683e4458cc98b68ca32841", size = 60077, upload-time = "2023-08-11T04:00:36.003Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/58/bdd5df067445478013f7a21b378181b206cc0aaf31024366ac813e0d9a96/hdrhistogram-0.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d5748a22ec68a5390f9d493aca933a6871788e34df91da4cc0a6ee19e336dc6d", size = 36664, upload-time = "2023-08-11T03:59:20.743Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ba/37b9144c0372b1f48b9310a8e4fc77a4d4f8949190b0e56ebc2dd17c9e54/hdrhistogram-0.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6d7e402365ced65309c3ffb060b6bcf7d1265bfba293509076f18b5d9ec260d", size = 48573, upload-time = "2023-08-11T03:59:22.259Z" }, - { url = "https://files.pythonhosted.org/packages/a9/22/8f1f52f3fa3291d7c1693d9266d31753be5f27b907c97ce4db495de169fa/hdrhistogram-0.10.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f55fcbd39953b8989344cfb56cfa06094dbffc3fd4df1ff05d4b15658e1bf6d", size = 47855, upload-time = "2023-08-11T03:59:23.328Z" }, - { url = "https://files.pythonhosted.org/packages/93/14/20cb3a638284a5903492eecb5b5d1303aa1ec9606b9e2296ca1753df1f0c/hdrhistogram-0.10.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d814811d52e699426a8b54f2448ab5e49fee3519a200cd887fd3faaaa6f4a35d", size = 53807, upload-time = "2023-08-11T03:59:24.441Z" }, - { url = "https://files.pythonhosted.org/packages/d6/99/a26df64d5069984e38305a6d6462534722f09c7f7578e5303903192f7a6a/hdrhistogram-0.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bfd6ad77c1f7806aaeb6b340866a6bb38a1f0fe94d8f5a5f74372c33a094913f", size = 52854, upload-time = "2023-08-11T03:59:26.42Z" }, - { url = "https://files.pythonhosted.org/packages/46/d9/7e9b72f217014fe9863b84b326af6d8ef4e559493f93ca10d81e53cbf2e2/hdrhistogram-0.10.3-cp311-cp311-win32.whl", hash = "sha256:28950b3ffaa97e859f76a08932a6c2a5baeca2a140804e0fd03b3b1d622a6c92", size = 39609, upload-time = "2023-08-11T03:59:27.985Z" }, - { url = "https://files.pythonhosted.org/packages/d1/54/72918ace22fbb247eae9cd61648c1a4142539e216764327721deb281d0de/hdrhistogram-0.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:e07dc9d667c71b061cc56a721f0005d8d77cf1a7f383902657703ac3ecd026f6", size = 40076, upload-time = "2023-08-11T03:59:29.547Z" }, - { url = "https://files.pythonhosted.org/packages/05/60/4d12ce18d95c815553751ace3936bccc54d67f47c7a2ebcd94c7fc89ca7f/hdrhistogram-0.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:088d3ef64c2004fc3cd4b21c4292efe4648367a1ce98c554bf7c5730a0ba018e", size = 36661, upload-time = "2023-08-11T03:59:31.173Z" }, - { url = "https://files.pythonhosted.org/packages/d0/20/10edd9915fcad1bd87c062c5c049a536d9783ebadd4e7f606414bdb74ce5/hdrhistogram-0.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8ae7ab424e6f2221ae9daed20610becb5d59cae2d448a05077b00e864c9e7", size = 48686, upload-time = "2023-08-11T03:59:32.294Z" }, - { url = "https://files.pythonhosted.org/packages/b1/8a/ca7b687c70409aec9a524e3ce7c044274f5108fd9c33cc93635237279b70/hdrhistogram-0.10.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2ba2550e8a392a543e727a4875f76f7131d1dd04ebe7c03d3cbe44b83fc130b", size = 47987, upload-time = "2023-08-11T03:59:33.84Z" }, - { url = "https://files.pythonhosted.org/packages/54/f5/1367cb6ef66d3d8c5e5091d8738d47a1f42414605b1638dd6785d23b9f99/hdrhistogram-0.10.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:57d61fd8378212d3d24149a331f770278766db541373d20a12f9399788ffde82", size = 53500, upload-time = "2023-08-11T03:59:35.132Z" }, - { url = "https://files.pythonhosted.org/packages/a4/9d/c3ba5788f3feed8b2198a8a5461706f174912bb59595af616595a7cefd98/hdrhistogram-0.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ad6d3ca8bcec581b8cf936608f79f6dd619e2690d1135c1978d80b01318e19e3", size = 52533, upload-time = "2023-08-11T03:59:37.027Z" }, - { url = "https://files.pythonhosted.org/packages/bd/ec/a41ade1c98bb4626f0ec95a5c56394b6e84b37e004338bd5b9cc24c61e29/hdrhistogram-0.10.3-cp312-cp312-win32.whl", hash = "sha256:90bf599703cd146b430fd4c111fb1290da902746ddea9c591c1cfb8313d37974", size = 39607, upload-time = "2023-08-11T03:59:38.166Z" }, - { url = "https://files.pythonhosted.org/packages/b7/05/f0e073f6ddabd71270135be8d1f5e7243e7c030f7468ef832d21c59eac54/hdrhistogram-0.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:92f0a43d0918ee6c48c78097c6e51eced260d0ae459c00a8a3690fbd9a06dc78", size = 40070, upload-time = "2023-08-11T03:59:39.167Z" }, -] - -[[package]] -name = "hf-xet" -version = "1.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/92/ec9ad04d0b5728dca387a45af7bc98fbb0d73b2118759f5f6038b61a57e8/hf_xet-1.4.3.tar.gz", hash = "sha256:8ddedb73c8c08928c793df2f3401ec26f95be7f7e516a7bee2fbb546f6676113", size = 670477, upload-time = "2026-03-31T22:40:07.874Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/43/724d307b34e353da0abd476e02f72f735cdd2bc86082dee1b32ea0bfee1d/hf_xet-1.4.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:7551659ba4f1e1074e9623996f28c3873682530aee0a846b7f2f066239228144", size = 3800935, upload-time = "2026-03-31T22:39:49.618Z" }, - { url = "https://files.pythonhosted.org/packages/2b/d2/8bee5996b699262edb87dbb54118d287c0e1b2fc78af7cdc41857ba5e3c4/hf_xet-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bee693ada985e7045997f05f081d0e12c4c08bd7626dc397f8a7c487e6c04f7f", size = 3558942, upload-time = "2026-03-31T22:39:47.938Z" }, - { url = "https://files.pythonhosted.org/packages/c3/a1/e993d09cbe251196fb60812b09a58901c468127b7259d2bf0f68bf6088eb/hf_xet-1.4.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21644b404bb0100fe3857892f752c4d09642586fd988e61501c95bbf44b393a3", size = 4207657, upload-time = "2026-03-31T22:39:39.69Z" }, - { url = "https://files.pythonhosted.org/packages/64/44/9eb6d21e5c34c63e5e399803a6932fa983cabdf47c0ecbcfe7ea97684b8c/hf_xet-1.4.3-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:987f09cfe418237812896a6736b81b1af02a3a6dcb4b4944425c4c4fca7a7cf8", size = 3986765, upload-time = "2026-03-31T22:39:37.936Z" }, - { url = "https://files.pythonhosted.org/packages/ea/7b/8ad6f16fdb82f5f7284a34b5ec48645bd575bdcd2f6f0d1644775909c486/hf_xet-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:60cf7fc43a99da0a853345cf86d23738c03983ee5249613a6305d3e57a5dca74", size = 4188162, upload-time = "2026-03-31T22:39:58.382Z" }, - { url = "https://files.pythonhosted.org/packages/1b/c4/39d6e136cbeea9ca5a23aad4b33024319222adbdc059ebcda5fc7d9d5ff4/hf_xet-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2815a49a7a59f3e2edf0cf113ae88e8cb2ca2a221bf353fb60c609584f4884d4", size = 4424525, upload-time = "2026-03-31T22:40:00.225Z" }, - { url = "https://files.pythonhosted.org/packages/46/f2/adc32dae6bdbc367853118b9878139ac869419a4ae7ba07185dc31251b76/hf_xet-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:42ee323265f1e6a81b0e11094564fb7f7e0ec75b5105ffd91ae63f403a11931b", size = 3671610, upload-time = "2026-03-31T22:40:10.42Z" }, - { url = "https://files.pythonhosted.org/packages/e2/19/25d897dcc3f81953e0c2cde9ec186c7a0fee413eb0c9a7a9130d87d94d3a/hf_xet-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:27c976ba60079fb8217f485b9c5c7fcd21c90b0367753805f87cb9f3cdc4418a", size = 3528529, upload-time = "2026-03-31T22:40:09.106Z" }, - { url = "https://files.pythonhosted.org/packages/ec/36/3e8f85ca9fe09b8de2b2e10c63b3b3353d7dda88a0b3d426dffbe7b8313b/hf_xet-1.4.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:5251d5ece3a81815bae9abab41cf7ddb7bcb8f56411bce0827f4a3071c92fdc6", size = 3801019, upload-time = "2026-03-31T22:39:56.651Z" }, - { url = "https://files.pythonhosted.org/packages/b5/9c/defb6cb1de28bccb7bd8d95f6e60f72a3d3fa4cb3d0329c26fb9a488bfe7/hf_xet-1.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1feb0f3abeacee143367c326a128a2e2b60868ec12a36c225afb1d6c5a05e6d2", size = 3558746, upload-time = "2026-03-31T22:39:54.766Z" }, - { url = "https://files.pythonhosted.org/packages/c1/bd/8d001191893178ff8e826e46ad5299446e62b93cd164e17b0ffea08832ec/hf_xet-1.4.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8b301fc150290ca90b4fccd079829b84bb4786747584ae08b94b4577d82fb791", size = 4207692, upload-time = "2026-03-31T22:39:46.246Z" }, - { url = "https://files.pythonhosted.org/packages/ce/48/6790b402803250e9936435613d3a78b9aaeee7973439f0918848dde58309/hf_xet-1.4.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d972fbe95ddc0d3c0fc49b31a8a69f47db35c1e3699bf316421705741aab6653", size = 3986281, upload-time = "2026-03-31T22:39:44.648Z" }, - { url = "https://files.pythonhosted.org/packages/51/56/ea62552fe53db652a9099eda600b032d75554d0e86c12a73824bfedef88b/hf_xet-1.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c5b48db1ee344a805a1b9bd2cda9b6b65fe77ed3787bd6e87ad5521141d317cd", size = 4187414, upload-time = "2026-03-31T22:40:04.951Z" }, - { url = "https://files.pythonhosted.org/packages/7d/f5/bc1456d4638061bea997e6d2db60a1a613d7b200e0755965ec312dc1ef79/hf_xet-1.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:22bdc1f5fb8b15bf2831440b91d1c9bbceeb7e10c81a12e8d75889996a5c9da8", size = 4424368, upload-time = "2026-03-31T22:40:06.347Z" }, - { url = "https://files.pythonhosted.org/packages/e4/76/ab597bae87e1f06d18d3ecb8ed7f0d3c9a37037fc32ce76233d369273c64/hf_xet-1.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:0392c79b7cf48418cd61478c1a925246cf10639f4cd9d94368d8ca1e8df9ea07", size = 3672280, upload-time = "2026-03-31T22:40:16.401Z" }, - { url = "https://files.pythonhosted.org/packages/62/05/2e462d34e23a09a74d73785dbed71cc5dbad82a72eee2ad60a72a554155d/hf_xet-1.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:681c92a07796325778a79d76c67011764ecc9042a8c3579332b61b63ae512075", size = 3528945, upload-time = "2026-03-31T22:40:14.995Z" }, - { url = "https://files.pythonhosted.org/packages/ac/9f/9c23e4a447b8f83120798f9279d0297a4d1360bdbf59ef49ebec78fe2545/hf_xet-1.4.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:d0da85329eaf196e03e90b84c2d0aca53bd4573d097a75f99609e80775f98025", size = 3805048, upload-time = "2026-03-31T22:39:53.105Z" }, - { url = "https://files.pythonhosted.org/packages/0b/f8/7aacb8e5f4a7899d39c787b5984e912e6c18b11be136ef13947d7a66d265/hf_xet-1.4.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:e23717ce4186b265f69afa66e6f0069fe7efbf331546f5c313d00e123dc84583", size = 3562178, upload-time = "2026-03-31T22:39:51.295Z" }, - { url = "https://files.pythonhosted.org/packages/df/9a/a24b26dc8a65f0ecc0fe5be981a19e61e7ca963b85e062c083f3a9100529/hf_xet-1.4.3-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc360b70c815bf340ed56c7b8c63aacf11762a4b099b2fe2c9bd6d6068668c08", size = 4212320, upload-time = "2026-03-31T22:39:42.922Z" }, - { url = "https://files.pythonhosted.org/packages/53/60/46d493db155d2ee2801b71fb1b0fd67696359047fdd8caee2c914cc50c79/hf_xet-1.4.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:39f2d2e9654cd9b4319885733993807aab6de9dfbd34c42f0b78338d6617421f", size = 3991546, upload-time = "2026-03-31T22:39:41.335Z" }, - { url = "https://files.pythonhosted.org/packages/bc/f5/067363e1c96c6b17256910830d1b54099d06287e10f4ec6ec4e7e08371fc/hf_xet-1.4.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:49ad8a8cead2b56051aa84d7fce3e1335efe68df3cf6c058f22a65513885baac", size = 4193200, upload-time = "2026-03-31T22:40:01.936Z" }, - { url = "https://files.pythonhosted.org/packages/42/4b/53951592882d9c23080c7644542fda34a3813104e9e11fa1a7d82d419cb8/hf_xet-1.4.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7716d62015477a70ea272d2d68cd7cad140f61c52ee452e133e139abfe2c17ba", size = 4429392, upload-time = "2026-03-31T22:40:03.492Z" }, - { url = "https://files.pythonhosted.org/packages/8a/21/75a6c175b4e79662ad8e62f46a40ce341d8d6b206b06b4320d07d55b188c/hf_xet-1.4.3-cp37-abi3-win_amd64.whl", hash = "sha256:6b591fcad34e272a5b02607485e4f2a1334aebf1bc6d16ce8eb1eb8978ac2021", size = 3677359, upload-time = "2026-03-31T22:40:13.619Z" }, - { url = "https://files.pythonhosted.org/packages/8a/7c/44314ecd0e89f8b2b51c9d9e5e7a60a9c1c82024ac471d415860557d3cd8/hf_xet-1.4.3-cp37-abi3-win_arm64.whl", hash = "sha256:7c2c7e20bcfcc946dc67187c203463f5e932e395845d098cc2a93f5b67ca0b47", size = 3533664, upload-time = "2026-03-31T22:40:12.152Z" }, -] - -[[package]] -name = "hpack" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276, upload-time = "2025-01-22T21:44:58.347Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" }, -] - -[[package]] -name = "htbuilder" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/88/0f48d1125168e9eea33879ea820000702533d103f1123d27ca69839a0db4/htbuilder-0.9.0.tar.gz", hash = "sha256:58c0bc5502c1a46b42ae9e074c43ec0f6fdc24ed334936cb17e1ed5a8938aee2", size = 10591, upload-time = "2025-01-09T07:00:26.189Z" } - -[[package]] -name = "httpcore" -version = "1.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, -] - -[[package]] -name = "httpr" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/eb/644349f01f0e8af737a52aff18f69c8490922ed890ed2c256d8d5db0c6c4/httpr-0.4.1.tar.gz", hash = "sha256:4a592c26b775e3542ea0fea069dd2cd9e9dc5a80e24e1fa2a466d5ac374eb03c", size = 567261, upload-time = "2026-03-21T07:41:37.912Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/9e/6babaca23d526b8c32232919104def1d81cd503741896a2be9e8ba9f0d9a/httpr-0.4.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:134a8b0042ab6b102a141c717219994ae9aa89e88ad97a09c8702f790225a623", size = 2458573, upload-time = "2026-03-21T07:41:21.009Z" }, - { url = "https://files.pythonhosted.org/packages/c5/81/271cf119550d684cab56101807540458f801f8c2c51f44d287ea59047031/httpr-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:524216fd1d688c78b0a523c6fe5220198789ddc27f4576d2a43a34ff7b7dbfe7", size = 2284320, upload-time = "2026-03-21T07:41:17.336Z" }, - { url = "https://files.pythonhosted.org/packages/36/f0/d093b7f779eb3cf97148c377c046d4408ff569332db3fb3ffa70a90978ff/httpr-0.4.1-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fd00861c9271056d4e270e8d8ca764ada592e4b1c451d3a5d7558f59fbae35d1", size = 2496084, upload-time = "2026-03-21T07:41:08.694Z" }, - { url = "https://files.pythonhosted.org/packages/70/4d/19234dda67df6a552c0776c4987d875cb1a77190a0bd4e418cb558b44d63/httpr-0.4.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6fcd417f838cb05ccf538f08fb3ed91586dad2641f4285a2b36b312405bc85d", size = 2528763, upload-time = "2026-03-21T07:40:59.741Z" }, - { url = "https://files.pythonhosted.org/packages/3e/cc/ea799133ebae7922a10e161a5d0d50099035b999d03f11d5cbff7d8d3b10/httpr-0.4.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:724edde6bd87e86f64b6581bb94690adbf73f5eb6d5f207b2adeece53069fcbd", size = 2391113, upload-time = "2026-03-21T07:41:04.198Z" }, - { url = "https://files.pythonhosted.org/packages/85/43/f48b3f7f064f06435f002d33298c8d2e09967c29135f959011daada6c3c6/httpr-0.4.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83a702fd09af35bc03b95edde0d1eafa5f2ed46750e0f240a75b370452ba6782", size = 2614235, upload-time = "2026-03-21T07:41:13.141Z" }, - { url = "https://files.pythonhosted.org/packages/69/33/28787212ac8e0e166a7a06decaa9531d1840ed68698f2e10c3372ce13a60/httpr-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:91e96795f20b53e54cdb031013134de87b7995d3bfa51ecc4aa26de9311b3653", size = 2704440, upload-time = "2026-03-21T07:41:25.228Z" }, - { url = "https://files.pythonhosted.org/packages/07/78/ce957ec65331b375a276471856bea3345c7e797e1f700e4f038527d9b3a8/httpr-0.4.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:46f9b006d3c61f597f3f7af03b69827bd1a14de6a6adfca28f6b9a5e3ad3dc23", size = 2649805, upload-time = "2026-03-21T07:41:28.814Z" }, - { url = "https://files.pythonhosted.org/packages/6e/93/c87b7e11e644948fd83f853b71e3d0989b217592cfc2efb37bbd4e835bd1/httpr-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4cb71034ef883243572debc078726e011a586932450951929d707e429aa509f6", size = 2859262, upload-time = "2026-03-21T07:41:33.622Z" }, - { url = "https://files.pythonhosted.org/packages/85/1a/bbde0bac4410178b895100c986346681e7b216d6f09d367270b692cb8e2c/httpr-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:637d58b3338656ae68d04df938db3028cf83a217874da92d94a0fe85ae286ecc", size = 2075114, upload-time = "2026-03-21T07:41:43.364Z" }, - { url = "https://files.pythonhosted.org/packages/ee/05/f9652691effe48ac17c75372716cb6361245a4f8f7d70129dd7b72bd33e5/httpr-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:cd4497483a78f4a10eaa9d67211a44176b7aec7a7de288228b7f1e3e0b958b0d", size = 2334107, upload-time = "2026-03-21T07:41:39.248Z" }, - { url = "https://files.pythonhosted.org/packages/f1/ee/7f654baa4061a88e9f4f156bea797783a4ba28929cf620778e7813ce19d5/httpr-0.4.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:13f1e2768d2c1543c874119beef2f68e98a9452c7c076038833ca03a60fc2e5c", size = 2460211, upload-time = "2026-03-21T07:41:22.512Z" }, - { url = "https://files.pythonhosted.org/packages/61/c4/3adb4b708cc2f5b44110f31dc217f292a0b33e71f6ed0d0c2211dcb03236/httpr-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:12db774b08250742183f9e4ae45695c64f832627f4c5f268cb59aba1e5c360b1", size = 2285243, upload-time = "2026-03-21T07:41:18.466Z" }, - { url = "https://files.pythonhosted.org/packages/e8/fc/c4c6755d38f4a97302992da1a5a9aa8d01e5ec180c09c47b93e2a7a9f85a/httpr-0.4.1-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1e610d73c813ea8c5b65b3830459f9aa8a5ce29afd4e50548e4706c1dc270405", size = 2496899, upload-time = "2026-03-21T07:41:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/bc/16/2e62a445588f0a4f832c4f3a8cb3e3d24ade52fdf59ef5ad426ad8fcf090/httpr-0.4.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:567ad9d8d24e889a06863ac6108f7bac7960b0cacd74ead04608bb9baaf44444", size = 2530318, upload-time = "2026-03-21T07:41:01.137Z" }, - { url = "https://files.pythonhosted.org/packages/a8/bf/92a47a0ffdfc7729786b00743dbfce34d7d2bab3ee3bde3ca17aa3413d52/httpr-0.4.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d77c62647bf6308d308b8352ac5132d96ab58f58b93c39bdae340a2fc5de0d44", size = 2392462, upload-time = "2026-03-21T07:41:05.484Z" }, - { url = "https://files.pythonhosted.org/packages/0c/8f/1bfa9a6767244b79df82c202c2843b664f9fc1acff9dbe7f982c8627d2fe/httpr-0.4.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a070723b04c9e41b4b8d6450f97dbf54820288d33efe740525f691d47c4af967", size = 2616728, upload-time = "2026-03-21T07:41:14.327Z" }, - { url = "https://files.pythonhosted.org/packages/6b/92/ba4b17be7e4060bf36e7472e2afa998eb87eeb168cfb0ce5aec0313b37d3/httpr-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ece5a1207c654080af757364c7fa0c4bbd25ff910db0f240a4d4b89d4c1c88a6", size = 2705345, upload-time = "2026-03-21T07:41:26.455Z" }, - { url = "https://files.pythonhosted.org/packages/11/d7/0d9fa9d695e2dde009d01d9380c1dfcbd6c1dc6b6e73c14c5ec615d36bb0/httpr-0.4.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d735b76da08fe88f57b914dbf03a790af7fc555eb2d343ee5faa27f90d0f07da", size = 2650374, upload-time = "2026-03-21T07:41:30.543Z" }, - { url = "https://files.pythonhosted.org/packages/ea/be/83219b05e6deb73164f3a1293dd1e534e1022bf20256f0908110d35338b8/httpr-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:14e6199419478540f98c66ea84c518623d1fa6a831a977020c3c7e0b68a5171f", size = 2861062, upload-time = "2026-03-21T07:41:35.19Z" }, - { url = "https://files.pythonhosted.org/packages/3c/af/1539ed1699e7d145f735fd1a9c726ee04c0bbeb092bf113d86320b1e269f/httpr-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:45c89be444b92bfad2e8d217045d07a1db7671b66df57f6bb1372e29db78ac2d", size = 2076326, upload-time = "2026-03-21T07:41:44.579Z" }, - { url = "https://files.pythonhosted.org/packages/3b/cf/07a68ebbae6397680bd6610fed4ad5658af7b42dabb3cc8b4bdb34efc850/httpr-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:419c4894a4547829636abb0ee98a8d27de4bf96555a354b8bc8c559f56e0e0a7", size = 2336305, upload-time = "2026-03-21T07:41:40.862Z" }, - { url = "https://files.pythonhosted.org/packages/73/02/b41c7b05d8ed68895b300d7caf2d72c4b45dc535b4e881f2ab58373ff3b6/httpr-0.4.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8ac0781cf3811c4804d8fc8092e535350dd1f9ead1678d06c05bf41c027526b6", size = 2461678, upload-time = "2026-03-21T07:41:23.942Z" }, - { url = "https://files.pythonhosted.org/packages/99/ca/496a46b119b0c37fe245dd1986248343a62f31bf5a601a8b2bfefb21b3ba/httpr-0.4.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:e0eb815d995826c1f0a82f3bad837bc90c53ccdcc3aeced1e8b49afcec31cdec", size = 2287700, upload-time = "2026-03-21T07:41:19.838Z" }, - { url = "https://files.pythonhosted.org/packages/30/3b/c9c31136156b35b0ad9db04dd2117ec5a13ad885ec51f3b0b3bd4fba43f4/httpr-0.4.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30889f270a2dbffdfa1575843c021909c2f38ceb576e89f7992a45ade097d262", size = 2394583, upload-time = "2026-03-21T07:41:07.221Z" }, - { url = "https://files.pythonhosted.org/packages/71/56/11acf0ec67166ed5aa17ae53c563bbe9dc5781527ce03d23dbe7e580c4e8/httpr-0.4.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:81162019e2308af4f0694d154a8286f99ef8cef5f8ef9b08aa6699288e2059cb", size = 2706538, upload-time = "2026-03-21T07:41:27.661Z" }, - { url = "https://files.pythonhosted.org/packages/d8/9f/94e93f742d302dcb00ff4a11b2da012983e886ad71508bd7f07a51fb71f0/httpr-0.4.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:ad2b8c324f856e061e5b242cae9eaedaf460c166f1fd7ce456a5872860d3d81a", size = 2652945, upload-time = "2026-03-21T07:41:31.782Z" }, - { url = "https://files.pythonhosted.org/packages/e6/a0/2986108c3d5ff7ea39587fae0f3eac984d7fc03f1975fc129e1eaa779846/httpr-0.4.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0d61f8c95d18498d90052048f890b611dacf3aa646d5dea68bdaf295d5bdd0c0", size = 2861039, upload-time = "2026-03-21T07:41:36.366Z" }, - { url = "https://files.pythonhosted.org/packages/94/da/084472a759715208f620424a08322b40569b40239c44a31e74b98e4c9a8a/httpr-0.4.1-cp39-abi3-win32.whl", hash = "sha256:5c6ea2a1151cc665faa6f6dae017a86d380590d5dbd0fd4321dbf1616f8d7660", size = 2077130, upload-time = "2026-03-21T07:41:45.774Z" }, - { url = "https://files.pythonhosted.org/packages/55/c1/149c9b3071339da353422602c73562062b3f11f2a019cecd20df8bc9f2a0/httpr-0.4.1-cp39-abi3-win_amd64.whl", hash = "sha256:cc2da5229e200e08e7858b55df99ddd33e48fba450f484fde0a035ea627dbf20", size = 2335329, upload-time = "2026-03-21T07:41:42.198Z" }, -] - -[[package]] -name = "httptools" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/08/17e07e8d89ab8f343c134616d72eebfe03798835058e2ab579dcc8353c06/httptools-0.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:474d3b7ab469fefcca3697a10d11a32ee2b9573250206ba1e50d5980910da657", size = 206521, upload-time = "2025-10-10T03:54:31.002Z" }, - { url = "https://files.pythonhosted.org/packages/aa/06/c9c1b41ff52f16aee526fd10fbda99fa4787938aa776858ddc4a1ea825ec/httptools-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3c3b7366bb6c7b96bd72d0dbe7f7d5eead261361f013be5f6d9590465ea1c70", size = 110375, upload-time = "2025-10-10T03:54:31.941Z" }, - { url = "https://files.pythonhosted.org/packages/cc/cc/10935db22fda0ee34c76f047590ca0a8bd9de531406a3ccb10a90e12ea21/httptools-0.7.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:379b479408b8747f47f3b253326183d7c009a3936518cdb70db58cffd369d9df", size = 456621, upload-time = "2025-10-10T03:54:33.176Z" }, - { url = "https://files.pythonhosted.org/packages/0e/84/875382b10d271b0c11aa5d414b44f92f8dd53e9b658aec338a79164fa548/httptools-0.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cad6b591a682dcc6cf1397c3900527f9affef1e55a06c4547264796bbd17cf5e", size = 454954, upload-time = "2025-10-10T03:54:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/44f89b280f7e46c0b1b2ccee5737d46b3bb13136383958f20b580a821ca0/httptools-0.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eb844698d11433d2139bbeeb56499102143beb582bd6c194e3ba69c22f25c274", size = 440175, upload-time = "2025-10-10T03:54:35.942Z" }, - { url = "https://files.pythonhosted.org/packages/6f/7e/b9287763159e700e335028bc1824359dc736fa9b829dacedace91a39b37e/httptools-0.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec", size = 440310, upload-time = "2025-10-10T03:54:37.1Z" }, - { url = "https://files.pythonhosted.org/packages/b3/07/5b614f592868e07f5c94b1f301b5e14a21df4e8076215a3bccb830a687d8/httptools-0.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:135fbe974b3718eada677229312e97f3b31f8a9c8ffa3ae6f565bf808d5b6bcb", size = 86875, upload-time = "2025-10-10T03:54:38.421Z" }, - { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" }, - { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" }, - { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" }, - { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" }, - { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" }, - { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" }, - { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" }, - { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" }, - { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" }, - { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" }, - { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" }, - { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" }, - { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" }, - { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, - { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, - { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, - { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, - { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, - { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, -] - -[[package]] -name = "httpx" -version = "0.28.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "certifi" }, - { name = "httpcore" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, -] - -[package.optional-dependencies] -http2 = [ - { name = "h2" }, -] - -[[package]] -name = "huggingface-hub" -version = "1.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, - { name = "httpx" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "tqdm" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e4/28/baf5d745559503ce8d28cf5bc9551f5ac59158eafd7b6a6afff0bcdb0f50/huggingface_hub-1.10.1.tar.gz", hash = "sha256:696c53cf9c2ac9befbfb5dd41d05392a031c69fc6930d1ed9671debd405b6fff", size = 758094, upload-time = "2026-04-09T15:01:18.928Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/8c/c7a33f3efaa8d6a5bc40e012e5ecc2d72c2e6124550ca9085fe0ceed9993/huggingface_hub-1.10.1-py3-none-any.whl", hash = "sha256:6b981107a62fbe68c74374418983399c632e35786dcd14642a9f2972633c8b5a", size = 642630, upload-time = "2026-04-09T15:01:17.35Z" }, -] - -[[package]] -name = "hyperframe" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-resources" -version = "6.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "itsdangerous" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "jmespath" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3c/56/3f325b1eef9791759784aa5046a8f6a1aff8f7c898a2e34506771d3b99d8/jmespath-0.10.0.tar.gz", hash = "sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9", size = 21607, upload-time = "2020-05-12T22:03:47.267Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/cb/5f001272b6faeb23c1c9e0acc04d48eaaf5c862c17709d20e3469c6e0139/jmespath-0.10.0-py2.py3-none-any.whl", hash = "sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f", size = 24489, upload-time = "2020-05-12T22:03:45.643Z" }, -] - -[[package]] -name = "joblib" -version = "1.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "kiwisolver" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798, upload-time = "2026-03-09T13:12:58.963Z" }, - { url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216, upload-time = "2026-03-09T13:13:00.155Z" }, - { url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911, upload-time = "2026-03-09T13:13:01.671Z" }, - { url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209, upload-time = "2026-03-09T13:13:03.385Z" }, - { url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888, upload-time = "2026-03-09T13:13:05.231Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304, upload-time = "2026-03-09T13:13:06.743Z" }, - { url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650, upload-time = "2026-03-09T13:13:08.607Z" }, - { url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949, upload-time = "2026-03-09T13:13:10.299Z" }, - { url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125, upload-time = "2026-03-09T13:13:11.841Z" }, - { url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783, upload-time = "2026-03-09T13:13:14.399Z" }, - { url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726, upload-time = "2026-03-09T13:13:16.047Z" }, - { url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738, upload-time = "2026-03-09T13:13:17.897Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718, upload-time = "2026-03-09T13:13:19.421Z" }, - { url = "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", size = 73480, upload-time = "2026-03-09T13:13:20.818Z" }, - { url = "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", size = 64930, upload-time = "2026-03-09T13:13:21.997Z" }, - { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" }, - { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" }, - { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" }, - { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" }, - { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" }, - { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" }, - { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" }, - { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" }, - { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" }, - { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" }, - { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" }, - { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" }, - { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" }, - { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" }, - { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" }, - { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166, upload-time = "2026-03-09T13:13:48.032Z" }, - { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395, upload-time = "2026-03-09T13:13:49.365Z" }, - { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065, upload-time = "2026-03-09T13:13:50.562Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903, upload-time = "2026-03-09T13:13:52.084Z" }, - { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751, upload-time = "2026-03-09T13:13:54.673Z" }, - { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793, upload-time = "2026-03-09T13:13:56.287Z" }, - { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041, upload-time = "2026-03-09T13:13:58.269Z" }, - { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292, upload-time = "2026-03-09T13:13:59.871Z" }, - { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865, upload-time = "2026-03-09T13:14:01.401Z" }, - { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369, upload-time = "2026-03-09T13:14:02.972Z" }, - { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989, upload-time = "2026-03-09T13:14:04.503Z" }, - { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645, upload-time = "2026-03-09T13:14:06.106Z" }, - { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237, upload-time = "2026-03-09T13:14:08.891Z" }, - { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573, upload-time = "2026-03-09T13:14:12.327Z" }, - { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998, upload-time = "2026-03-09T13:14:13.469Z" }, - { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700, upload-time = "2026-03-09T13:14:14.636Z" }, - { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537, upload-time = "2026-03-09T13:14:15.808Z" }, - { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514, upload-time = "2026-03-09T13:14:18.035Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848, upload-time = "2026-03-09T13:14:19.745Z" }, - { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542, upload-time = "2026-03-09T13:14:21.54Z" }, - { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447, upload-time = "2026-03-09T13:14:23.205Z" }, - { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918, upload-time = "2026-03-09T13:14:24.74Z" }, - { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856, upload-time = "2026-03-09T13:14:26.597Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580, upload-time = "2026-03-09T13:14:28.237Z" }, - { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018, upload-time = "2026-03-09T13:14:30.018Z" }, - { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804, upload-time = "2026-03-09T13:14:32.888Z" }, - { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" }, - { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" }, - { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231, upload-time = "2026-03-09T13:14:41.323Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489, upload-time = "2026-03-09T13:14:42.534Z" }, - { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063, upload-time = "2026-03-09T13:14:44.759Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913, upload-time = "2026-03-09T13:14:46.247Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782, upload-time = "2026-03-09T13:14:48.458Z" }, - { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815, upload-time = "2026-03-09T13:14:50.721Z" }, - { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925, upload-time = "2026-03-09T13:14:52.304Z" }, - { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322, upload-time = "2026-03-09T13:14:54.637Z" }, - { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857, upload-time = "2026-03-09T13:14:56.471Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376, upload-time = "2026-03-09T13:14:58.072Z" }, - { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549, upload-time = "2026-03-09T13:14:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680, upload-time = "2026-03-09T13:15:01.364Z" }, - { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905, upload-time = "2026-03-09T13:15:03.925Z" }, - { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086, upload-time = "2026-03-09T13:15:07.775Z" }, - { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577, upload-time = "2026-03-09T13:15:09.139Z" }, - { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794, upload-time = "2026-03-09T13:15:10.525Z" }, - { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646, upload-time = "2026-03-09T13:15:12.016Z" }, - { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511, upload-time = "2026-03-09T13:15:13.311Z" }, - { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858, upload-time = "2026-03-09T13:15:15.103Z" }, - { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539, upload-time = "2026-03-09T13:15:16.661Z" }, - { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310, upload-time = "2026-03-09T13:15:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244, upload-time = "2026-03-09T13:15:20.444Z" }, - { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154, upload-time = "2026-03-09T13:15:22.039Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377, upload-time = "2026-03-09T13:15:23.741Z" }, - { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288, upload-time = "2026-03-09T13:15:25.789Z" }, - { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158, upload-time = "2026-03-09T13:15:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260, upload-time = "2026-03-09T13:15:29.421Z" }, - { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403, upload-time = "2026-03-09T13:15:31.517Z" }, - { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687, upload-time = "2026-03-09T13:15:33.204Z" }, - { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032, upload-time = "2026-03-09T13:15:34.411Z" }, - { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" }, - { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" }, - { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" }, - { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" }, - { url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532, upload-time = "2026-03-09T13:15:47.047Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420, upload-time = "2026-03-09T13:15:48.199Z" }, - { url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892, upload-time = "2026-03-09T13:15:49.694Z" }, - { url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603, upload-time = "2026-03-09T13:15:50.945Z" }, - { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" }, -] - -[[package]] -name = "lancedb" -version = "0.24.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "deprecation" }, - { name = "numpy" }, - { name = "overrides" }, - { name = "packaging" }, - { name = "pyarrow" }, - { name = "pydantic" }, - { name = "tqdm" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/41/6e51c15c0ea9dc19c6eb037a0c284ccae49fc2e8425b934a6a1ccdc0e6d6/lancedb-0.24.3-cp39-abi3-macosx_10_15_x86_64.whl", hash = "sha256:65b5aa6201d6f1d921694dce614a2686c1272e25926cc7809ad8eb89a8eb85fa", size = 33401550, upload-time = "2025-08-15T19:02:35.274Z" }, - { url = "https://files.pythonhosted.org/packages/1b/78/d020464db8f189923caba69eaa9281c8b0a6c5c6cef3841cca3a17feb00e/lancedb-0.24.3-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f2b86928b3175afde9507ca2e39f545df380ca52c415511ffd52fab8b09937ec", size = 30810069, upload-time = "2025-08-15T19:15:55.771Z" }, - { url = "https://files.pythonhosted.org/packages/b7/30/3317ebf9c6397591b9beccd091a87e23f7d8186ddaa7cd0c1aa318e09323/lancedb-0.24.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7756d020c2e6d22130d8f59f32b816fbfda5f65ecc80c5b1bbeb01bf55dc834", size = 31708034, upload-time = "2025-08-15T18:23:36.783Z" }, - { url = "https://files.pythonhosted.org/packages/7b/61/b91a7bfe0138cec86f2e9930160112f38da923333da16f2bfaac85649d45/lancedb-0.24.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c933d2680e7bb28d99cb32b2395e808593a845a2b5ab59030e7d840d97e91f2", size = 34916331, upload-time = "2025-08-15T18:26:11.104Z" }, - { url = "https://files.pythonhosted.org/packages/e0/c6/b475d3addf841f803f7c54a64427ef0e973ce340295f59adbd0358097a69/lancedb-0.24.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:60ecfaabd1d33f435b498624f988e610cafacd5dfea26368e5582647730495e0", size = 31718200, upload-time = "2025-08-15T18:22:17.472Z" }, - { url = "https://files.pythonhosted.org/packages/9a/b9/3e0e25b7c6dcd4f6b0e977cb886965070ca05d7994819834484cbe8c8d00/lancedb-0.24.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:78632bd98e6c317609ce844eb737d1dd306786291ed2d98009e37377be13e1b4", size = 34963956, upload-time = "2025-08-15T18:25:47.58Z" }, - { url = "https://files.pythonhosted.org/packages/c4/3b/063e78eaf61ee8c1428e1063d9c29c5fab8b2d7f5d324a13716419a29a57/lancedb-0.24.3-cp39-abi3-win_amd64.whl", hash = "sha256:d5eb70b8a3b66d728c183f9b77ddfed13894c12880f647bf5cd85e477ad5368b", size = 36945181, upload-time = "2025-08-15T18:44:06.182Z" }, -] - -[[package]] -name = "lxml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/42/149c7747977db9d68faee960c1a3391eb25e94d4bb677f8e2df8328e4098/lxml-6.0.3.tar.gz", hash = "sha256:a1664c5139755df44cab3834f4400b331b02205d62d3fdcb1554f63439bf3372", size = 4237567, upload-time = "2026-04-09T14:39:09.664Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/ce/8a0b4747bb5dd47fec3443f0506a2a2d4f58946d7176bc3fdcae781ac666/lxml-6.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c8184fdb2259bda1db2db9d6e25f667769afc2531830b4fa29f83f66a7872dea", size = 8524445, upload-time = "2026-04-09T14:34:14.244Z" }, - { url = "https://files.pythonhosted.org/packages/e5/14/b74a06da69d212d1ac27e4bcf124e966d1d63c4d23522add86fbcf20324e/lxml-6.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b0f01fb8bdcaf4aa69cf55b2b2f8ef722e4423e1c020e7250dcb89a1d5db38e", size = 4594891, upload-time = "2026-04-09T14:34:17.123Z" }, - { url = "https://files.pythonhosted.org/packages/39/9a/364392e9740ddcdba380c5dbb79464956aadf81135344d57153631c8e4a2/lxml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fab00cef83d4f9d76c5e0722346e84bc174b071d68b4f725aeb0bf3877b9e6a6", size = 4922596, upload-time = "2026-04-09T14:34:19.632Z" }, - { url = "https://files.pythonhosted.org/packages/24/b6/6e4a53869a8e031dc5ea564a9857f6dd520a05412aea8d1b6565e8b2d43d/lxml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f753db5785ce019d7b25bb75638ef5a42a0e208aa9f19933262134e668ca6af", size = 5067033, upload-time = "2026-04-09T14:34:22.015Z" }, - { url = "https://files.pythonhosted.org/packages/2d/cc/12035c0d104fbe64e56e7b2cd9d4942ffa2a1689f093f44de0eef73538ae/lxml-6.0.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27e317e554bc6086a082688ddf137437e5f7f20ffdd736a6f5b4e3ed1ecf1247", size = 5000434, upload-time = "2026-04-09T14:34:23.934Z" }, - { url = "https://files.pythonhosted.org/packages/73/37/b9f9b28b542d0e62bb9353753cbec7e321f7394fea10470c6b8e5739b61d/lxml-6.0.3-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:feb5b9ed7d0510663a78b94f2b417a41c41b42a7bb157ef398ef9d78e6f0fd50", size = 5201705, upload-time = "2026-04-09T14:34:26.328Z" }, - { url = "https://files.pythonhosted.org/packages/2d/26/9473de56eb74293c7061ff1a6ac352d5b89c83067f315accd73cf97a3b07/lxml-6.0.3-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:51014ee2ab2091dcd9cdef92532f0a1addb7c2cc52a2bd70682e441363de5c0d", size = 5329269, upload-time = "2026-04-09T14:34:29.563Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a3/502f97b6221e0958da94fde5eb17119f2104694a88126ef82fa189d5d7a4/lxml-6.0.3-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:abc39c4fb67f029400608f9a3a4a3f351ccb3c061b05fd3ad113e4cfbba8a8ee", size = 4658312, upload-time = "2026-04-09T14:34:31.62Z" }, - { url = "https://files.pythonhosted.org/packages/d1/26/935d0297d1c272282e950986f14f044c8e4c34e60a8774bc993d26ddcf32/lxml-6.0.3-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:38652c280cf50cc5cf955e3d0b691fa6a97046d84407bbae340d8e353f9014ef", size = 5264811, upload-time = "2026-04-09T14:34:33.566Z" }, - { url = "https://files.pythonhosted.org/packages/bb/a0/4755420775ded42b4cc9017357ce72ee7cd08fbfb72da3ac7e48fa2326bb/lxml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c3de55b53f69ffa2fcfd897bd8a7e62f0f88a40a8a0c544e171e813f9d4ddbf5", size = 5043997, upload-time = "2026-04-09T14:34:35.506Z" }, - { url = "https://files.pythonhosted.org/packages/b7/54/61f21fcf0b5c0f30e58c369aacfa01f5a21ef0f8c9c773c413010c18a705/lxml-6.0.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bd4f70e091f2df300396bc9ce36963f90b87611324c2ca750072a6e6375beba2", size = 4711595, upload-time = "2026-04-09T14:34:38.195Z" }, - { url = "https://files.pythonhosted.org/packages/ca/9e/b9f73274a7e3819c821033ea9d8e777b297fcbe789765948d8c9d4fb9cfc/lxml-6.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c157bfef4e3b19688eb4da783c5bfabf5a3ac1ac8d317e0906f3feb18d4c89b7", size = 5251294, upload-time = "2026-04-09T14:34:40.461Z" }, - { url = "https://files.pythonhosted.org/packages/38/8c/73e463041bad522c348c8a8c908a63c32f80215cff596210bbf24d69b3ee/lxml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8d10a75e4d0a6a9ac2fec2f7ade682f468b51935102c70dab638fa4e94ffcb04", size = 5224927, upload-time = "2026-04-09T14:34:42.986Z" }, - { url = "https://files.pythonhosted.org/packages/3f/05/42820ad63897bfd35cb7e591d79e8d21524c9da1520fa156b71d32f6953b/lxml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:d573b81c29e20b1513afa386a544797a99cecde5497e6c77b6dfa4484112c819", size = 3593261, upload-time = "2026-04-09T14:34:44.804Z" }, - { url = "https://files.pythonhosted.org/packages/e1/04/43c561e2293ede683f5259ceaccaf24ad6e830631123f197c1db483439ba/lxml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:ac63a1ef1899ccadace10ac937c41321672771378374c254e931d001448ae372", size = 4023698, upload-time = "2026-04-09T14:34:46.845Z" }, - { url = "https://files.pythonhosted.org/packages/8e/5f/13fde57b45a0f88b8c4bb02156fb115e99ad48354029cb522b543f502563/lxml-6.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:10bc4f37c28b4e1b3e901dde66e3a096eb128acf388d5b2962dc2941284293bb", size = 3666947, upload-time = "2026-04-09T14:34:48.675Z" }, - { url = "https://files.pythonhosted.org/packages/ac/4c/552571c619edd607432cbbf25e312a5d02859f2a7de421494a644b48451e/lxml-6.0.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ad6952810349cbfb843fe15e8afc580b2712359ae42b1d2b05d097bd48c4aea4", size = 8570109, upload-time = "2026-04-09T14:34:50.969Z" }, - { url = "https://files.pythonhosted.org/packages/ac/49/cf08843a6a923cd1eef40797a31e61424ac257c43634b5c9cff3bee93696/lxml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b81ec1ecac3be8c1ff1e00ca1c1baf8122e87db9000cd2549963847bd5e3b41", size = 4623404, upload-time = "2026-04-09T14:34:53.79Z" }, - { url = "https://files.pythonhosted.org/packages/b6/59/ffde0037a781b10c854abdf9e34fbf60d8f375ce8026551982b9f26695cc/lxml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:448e69211e59c39f398990753d15ba49f7218ec128f64ac8012ef16762e509a3", size = 4929662, upload-time = "2026-04-09T14:34:55.763Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/c468055e45954a93e1bc043a964d327d6784552d6551dc2364a1f83c53a1/lxml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6289cb9145fbbc5b0e159c9fcd7fc09446dadc6b60b72c4d1012e80c7c727970", size = 5092106, upload-time = "2026-04-09T14:34:58.522Z" }, - { url = "https://files.pythonhosted.org/packages/59/a3/8400c79a6defe609e24ce7b580f48d53f08acbf4c998eede0083a89f16f0/lxml-6.0.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b68c29aac4788438b07d768057836de47589c7deaa3ad8dc4af488dfc27be388", size = 5004214, upload-time = "2026-04-09T14:35:00.531Z" }, - { url = "https://files.pythonhosted.org/packages/57/b5/797246619cd0831c8d239f91fd4683683abbe7144854c6f33c68a6ea9f42/lxml-6.0.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50293e024afe5e2c25da2be68c8ceca8618912a0701a73f75b488317c8081aa6", size = 5630889, upload-time = "2026-04-09T14:35:02.89Z" }, - { url = "https://files.pythonhosted.org/packages/a0/fa/b86302385dc896d02ebb2803e4522a923acaa30e6cb35223492257ee24ab/lxml-6.0.3-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac65c08ba1bd90f662cb1d5c79f7ae4c53b1c100f0bb6ec5df1f40ac29028a7e", size = 5237728, upload-time = "2026-04-09T14:35:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/9b/7d/812c054b7d15f4dfb3a6fc877c2936023fcd8ac8b53807f996c8c60c4f57/lxml-6.0.3-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:16fbcf06ae534b2fa5bcdc19fcf6abd9df2e74fe8563147d1c5a687a130efed4", size = 5349527, upload-time = "2026-04-09T14:35:08.121Z" }, - { url = "https://files.pythonhosted.org/packages/b8/4a/33a572874924809928747cd156b172b04cd19c1ec1d10925fc77dfeb676d/lxml-6.0.3-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:3a0484bd1e84f82766befcbd71cccd7307dacfe08071e4dbc1d9a9b498d321e8", size = 4693177, upload-time = "2026-04-09T14:35:10.4Z" }, - { url = "https://files.pythonhosted.org/packages/36/d5/71842813ca0c43718f641e770195e278832f8c01870eaac857a3de34448a/lxml-6.0.3-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c137f8c8419c3de93e2998131d94628805f148e52b34da6d7533454e4d78bc2a", size = 5243928, upload-time = "2026-04-09T14:35:12.393Z" }, - { url = "https://files.pythonhosted.org/packages/da/a7/330845ae467c6086ef35977c335bb252fa11490082335f9ccfd0465bdfb7/lxml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:775266571f7027b1d77f5fce18a247b24f51a4404bdc1b90ec56be9b1e3801b9", size = 5046937, upload-time = "2026-04-09T14:35:15.209Z" }, - { url = "https://files.pythonhosted.org/packages/02/3d/b58b0aee0cf7e0b7eb5d24795a129c634c6d07f032d8b902bb0859319d13/lxml-6.0.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:aa18653b795d2c273b8676f7ad2ca916d846d15864e335f746658e4c28eb5168", size = 4776758, upload-time = "2026-04-09T14:35:17.758Z" }, - { url = "https://files.pythonhosted.org/packages/8c/4c/f421b50f08c1b724a24c4a778db8888d0a2d948b4dd08b80f4f05a0804ff/lxml-6.0.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:cbffd22fc8e4d80454efa968b0c93440a00b8b8a817ce0c29d2c6cb5ad324362", size = 5644912, upload-time = "2026-04-09T14:35:20.438Z" }, - { url = "https://files.pythonhosted.org/packages/a7/99/eabfedb111ca1f26c8fe890413eabc7e2b0010f075fdf5bceb42737c3894/lxml-6.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7373ede7ccb89e6f6e39c1423b3a4d4ee48035d3b4619a6addced5c8b48d0ecc", size = 5233509, upload-time = "2026-04-09T14:35:23.137Z" }, - { url = "https://files.pythonhosted.org/packages/9f/17/050a105ca1154025b68c19901d45292cbdcee6f25bd056c178ad6b55e534/lxml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e759ff1b244725fef428c6b54f3dab4954c293b2d242a5f2e79db5cc3873de51", size = 5260150, upload-time = "2026-04-09T14:35:25.385Z" }, - { url = "https://files.pythonhosted.org/packages/61/a0/ed83517d12e9fe00101a21fe08a168fd69f57875d9416353e2a38c401df7/lxml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:f179bae37ad673f57756b59f26833b7922230bef471fdb29492428f152bae8c6", size = 3595160, upload-time = "2026-04-09T14:35:27.519Z" }, - { url = "https://files.pythonhosted.org/packages/55/d3/101726831f45951fe3ddd03cffbd2a4ac6261fc63ada399e6f7051d43af6/lxml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:8eeec925ad7f81886d413b3a1f8715551f75543519229a9b35e957771e1826d5", size = 3996108, upload-time = "2026-04-09T14:35:29.608Z" }, - { url = "https://files.pythonhosted.org/packages/49/9f/ab1c58ad55bfcd4b55bafd98f19ff24f34315441f13aa787d5220def0702/lxml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:f96bba9a26a064ce9e11099bad12fb08384b64d3acc0acf94bf386ca5cf4f95f", size = 3658906, upload-time = "2026-04-09T14:35:32.451Z" }, - { url = "https://files.pythonhosted.org/packages/86/a6/2cdc9c5a634b1b890927f968febc2474fa3eb6fed99db82ea3c008bbbda4/lxml-6.0.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:83c1d75e9d124ab82a4ddaf59135112f0dc49526b47355e5928ae6126a68e236", size = 8559579, upload-time = "2026-04-09T14:35:35.644Z" }, - { url = "https://files.pythonhosted.org/packages/97/3c/adfbcdab17f89f72e069c5df5661c81e0511e3cdb353550f778e9ffaa08e/lxml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b683665d0287308adafc90a5617a51a508d8af8c7040693693bb333b5f4474fe", size = 4617332, upload-time = "2026-04-09T14:35:38.901Z" }, - { url = "https://files.pythonhosted.org/packages/5e/d4/ee1a5c734a5ad79024fa85808f3efc18d5733813141e2bb2726a7d9d8bea/lxml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ed31e5852cd938704bc6c7a3822cbf84c7fa00ebfa914a1b4e2392d44f45bdfb", size = 4922821, upload-time = "2026-04-09T14:35:41.521Z" }, - { url = "https://files.pythonhosted.org/packages/f1/1f/87efcc0b93ba4f95303ec8f80164f3c50db20a3a5612a285133f9ad6cb7e/lxml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8922a30704a4421d69a19e0499db5861da686c0bccc3a79cf3946e3155cf25f9", size = 5081226, upload-time = "2026-04-09T14:35:44.02Z" }, - { url = "https://files.pythonhosted.org/packages/65/8b/fd0fadd9ec8a6ac9d694014ccdb9504e28705abb2e08c9ca23c609020325/lxml-6.0.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a1adb0e220cb8691202ba9d97646a06292657a122df4b92733861d42f7cf4d2", size = 4992884, upload-time = "2026-04-09T14:35:46.769Z" }, - { url = "https://files.pythonhosted.org/packages/68/75/2fb0e534225214c6386496b7847195d7297b913cf563c5ccea394afc346b/lxml-6.0.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:821fd53699eb498990c915ba955a392d07246454c9405e6c1d0692362503013d", size = 5613383, upload-time = "2026-04-09T14:35:49.303Z" }, - { url = "https://files.pythonhosted.org/packages/54/3a/8f560f8fb2f5f092e18ac7a13a94b77e0e5213fe7c424d12e98393dcc7d8/lxml-6.0.3-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:04b7cedf52e125f86d0d426635e7fbe8e353d4cc272a1757888e3c072424381d", size = 5228398, upload-time = "2026-04-09T14:35:51.611Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d5/6bf993c02a0173eb5883ace61958c55c245d3daf7753fb5f931a9691b440/lxml-6.0.3-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:9d98063e6ae0da5084ec46952bb0a5ccb5e2cad168e32b4d65d1ec84e4b4ebd4", size = 5342198, upload-time = "2026-04-09T14:35:54.311Z" }, - { url = "https://files.pythonhosted.org/packages/bb/18/637130349ca6aa33b6dc4796732835ede5017a811c5f55763a1c468f7971/lxml-6.0.3-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:ce01ab3449015358f766a1950b3d818eedf9d4cdec3fa87e4eecaad10c0784db", size = 4699178, upload-time = "2026-04-09T14:35:56.647Z" }, - { url = "https://files.pythonhosted.org/packages/bb/19/239daafcc1cfa42b8aa6384509a9fd2cb1aa281679c6e8395adf9ccbc189/lxml-6.0.3-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d38c25bad123d6ce30bb37931d90a4e8a167cd796eeae9cd16c2bfce52718f8e", size = 5231869, upload-time = "2026-04-09T14:36:00.41Z" }, - { url = "https://files.pythonhosted.org/packages/0a/74/db7fcadc651b988502bed00d48acfd8b997ecb5dd52ebcc05f39bf946d9e/lxml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9b8e0779780026979f217603385995202f364adc9807bd21210d81b9f562fc4e", size = 5043669, upload-time = "2026-04-09T14:36:02.463Z" }, - { url = "https://files.pythonhosted.org/packages/55/99/af795b579182fa04aa87fcb0bd112e22705d982f71eb53874a8d356b4091/lxml-6.0.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8c082ad2398664213a4bb5d133e2eb8bf239220b7d6688f8c8ffa9050057501f", size = 4769745, upload-time = "2026-04-09T14:36:04.716Z" }, - { url = "https://files.pythonhosted.org/packages/52/4d/10e652edc55d206188a1b738d1033aad3497886d34cb7f5fc753e67ecb49/lxml-6.0.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dfc80c74233fe01157ab550fb12b9d07a2f1fa7c5900cefb484e3bf02e856fbc", size = 5635496, upload-time = "2026-04-09T14:36:06.815Z" }, - { url = "https://files.pythonhosted.org/packages/ab/68/95371835ec15bb46feee27b090bcabbe579f4ad04efbef08e2713bcfea16/lxml-6.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5c45bdcdc2ca6cf26fddff3faa5de7a2ed7c7f6016b3de80125313a37f972378", size = 5223564, upload-time = "2026-04-09T14:36:09.057Z" }, - { url = "https://files.pythonhosted.org/packages/aa/a6/0a9e5b63e8959487551be5d5496bb758ed2424c77ed7b25a9b8aae3b60c6/lxml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99457524afd384c330dc51e527976653d543ccadfa815d9f2d92c5911626e536", size = 5250124, upload-time = "2026-04-09T14:36:11.337Z" }, - { url = "https://files.pythonhosted.org/packages/d9/80/de3d3a790edf6d026c829fe8ccf54845058f57f8bb788e420c3b227eecef/lxml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:c8e3b8a54e65393ce1d5c7d9753fe756f0d96089e7163b20ddec3e5bb56a963e", size = 3596004, upload-time = "2026-04-09T14:36:13.446Z" }, - { url = "https://files.pythonhosted.org/packages/9f/cf/43c9a5926060e39d99593921f37d7e88f129bc32ab6266b8460483abd613/lxml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:724b26a38cef98d6869d00a33cb66083bee967598e44f6a8e53f1dd283c851b0", size = 3994750, upload-time = "2026-04-09T14:36:15.686Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d3/b224dbc282bfef52d2e05645e405b5ed89c6391144dc09864229fe9ce88c/lxml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:f27373113fda6621e4201f529908a24c8a190c2af355aed4711dadca44db4673", size = 3657620, upload-time = "2026-04-09T14:36:17.952Z" }, - { url = "https://files.pythonhosted.org/packages/d3/40/b637359bacf3813f1174d15b08516020ba5beb355e04377105d561e6e00a/lxml-6.0.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8c08926678852a233bf1ef645c4d683d56107f814482f8f41b21ef2c7659790e", size = 8575318, upload-time = "2026-04-09T14:36:20.608Z" }, - { url = "https://files.pythonhosted.org/packages/7f/91/d5286a45202ed91f1e428e68c6e1c11bcb2b42715c48424871fc73485b05/lxml-6.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2ce76d113a7c3bf42761ec1de7ca615b0cbf9d8ae478eb1d6c20111d9c9fc098", size = 4623084, upload-time = "2026-04-09T14:36:24.015Z" }, - { url = "https://files.pythonhosted.org/packages/8a/5f/7ea1af571ee13ed1e5fba007fd83cd0794723ca76a51eed0ef9513363b1f/lxml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83eca62141314d641ebe8089ffa532bbf572ea07dd6255b58c40130d06bb2509", size = 4948797, upload-time = "2026-04-09T14:36:26.662Z" }, - { url = "https://files.pythonhosted.org/packages/82/be/3a9b8d787d9877cbe17e02ef5af2523bd14ecc177ce308397c485c56fe18/lxml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d8781d812bb8efd47c35651639da38980383ff0d0c1f3269ade23e3a90799079", size = 5085983, upload-time = "2026-04-09T14:36:29.486Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2b/645abaef837b11414c81513c31b308a001fb8cd370f665c3ebc854be5ba5/lxml-6.0.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19b079e81aa3a31b523a224b0dd46da4f56e1b1e248eef9a599e5c885c788813", size = 5031039, upload-time = "2026-04-09T14:36:31.735Z" }, - { url = "https://files.pythonhosted.org/packages/3b/4f/561f30b77e9edbb373e2b6b7203a7d6ab219c495abca219536c66f3a44b2/lxml-6.0.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6c055bafdcb53e7f9f75e22c009cd183dd410475e21c296d599531d7f03d1bf5", size = 5646718, upload-time = "2026-04-09T14:36:34.127Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ba/2a72e673d109b563c2ab77097f2f4ca64e2927d2f04836ba07aaabe1da0e/lxml-6.0.3-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f1594a183cee73f9a1dbfd35871c4e04b461f47eeb9bcf80f7d7856b1b136d", size = 5239360, upload-time = "2026-04-09T14:36:37.195Z" }, - { url = "https://files.pythonhosted.org/packages/52/98/4e5a4ef87d846af90cc9c1ee2f8af2af34c221e620aad317b3a535361b93/lxml-6.0.3-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:a6380c5035598e4665272ad3fc86c96ddb2a220d4059cce5ba4b660f78346ad9", size = 5351233, upload-time = "2026-04-09T14:36:39.634Z" }, - { url = "https://files.pythonhosted.org/packages/cb/b8/cff0af5fe48ede6b1949dc2e14171470c0c68a15789037c1fed90602b89d/lxml-6.0.3-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:143ac903fb6c9be6da613390825c8e8bb8c8d71517d43882031f6b9bc89770ef", size = 4696677, upload-time = "2026-04-09T14:36:42.037Z" }, - { url = "https://files.pythonhosted.org/packages/0c/6e/0b2a918fb15c30b00ff112df16c548df011db37b58d764bd17f47db74905/lxml-6.0.3-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4fff7d77f440378cd841e340398edf5dbefee334816efbf521bb6e31651e54e", size = 5250503, upload-time = "2026-04-09T14:36:44.417Z" }, - { url = "https://files.pythonhosted.org/packages/57/1b/4697918f9d4c2e643e2c59cedb37c2f3a9f76fb1217d767f6dff476813d8/lxml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:631567ffc3ddb989ccdcd28f6b9fa5aab1ec7fc0e99fe65572b006a6aad347e2", size = 5084563, upload-time = "2026-04-09T14:36:46.762Z" }, - { url = "https://files.pythonhosted.org/packages/7b/8c/d7ec96246f0632773912c6556288d3b6bb6580f3a967441ca4636ddc3f73/lxml-6.0.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:38acf7171535ffa7fff1fcec8b82ebd4e55cd02e581efe776928108421accaa1", size = 4737407, upload-time = "2026-04-09T14:36:49.826Z" }, - { url = "https://files.pythonhosted.org/packages/d2/0c/603e35bf77aeb28c972f39eece35e7c0f6579ff33a7bed095cc2f7f942d9/lxml-6.0.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:06b9f3ac459b4565bbaa97aa5512aa7f9a1188c662f0108364f288f6daf35773", size = 5670919, upload-time = "2026-04-09T14:36:52.231Z" }, - { url = "https://files.pythonhosted.org/packages/92/08/6d3f188e6705cf0bfd8b5788055c7381bb3ffa786dfba9fa0b0ed5778506/lxml-6.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2773dbe2cedee81f2769bd5d24ceb4037706cf032e1703513dd0e9476cd9375f", size = 5237771, upload-time = "2026-04-09T14:36:55.286Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4c/01639533b90e9ff622909c113df2ab2dbdd1d78540eb153d13b66a9c96ba/lxml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:30c437d8bb9a9a9edff27e85b694342e47a26a6abc249abe00584a4824f9d80d", size = 5263862, upload-time = "2026-04-09T14:36:58.247Z" }, - { url = "https://files.pythonhosted.org/packages/06/0e/bd1157d7b09d1f5e1d580c124203cee656130a3f8908365760a593b21daf/lxml-6.0.3-cp314-cp314-win32.whl", hash = "sha256:1b60a3a1205f869bd47874787c792087174453b1a869db4837bf5b3ff92be017", size = 3656378, upload-time = "2026-04-09T14:37:47.74Z" }, - { url = "https://files.pythonhosted.org/packages/c5/cc/d50cbce8cd5687670868bea33bbeefa0866c5e5d02c5e11c4a04c79fc45e/lxml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:5b6913a68d98c58c673667c864500ba31bc9b0f462effac98914e9a92ebacd2e", size = 4062518, upload-time = "2026-04-09T14:37:49.911Z" }, - { url = "https://files.pythonhosted.org/packages/fd/c7/ece11a1e51390502894838aa384e9f98af7bef4d6806a927197153a16972/lxml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:1b36a3c73f2a6d9c2bfae78089ca7aedae5c2ee5fd5214a15f00b2f89e558ba7", size = 3741064, upload-time = "2026-04-09T14:37:52.185Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ae/918d7f89635fb6456cd732c12246c0e504dd9c49e8006f3593c9ecdb90ff/lxml-6.0.3-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:239e9a6be3a79c03ec200d26f7bb17a4414704a208059e20050bf161e2d8848a", size = 8826590, upload-time = "2026-04-09T14:37:00.862Z" }, - { url = "https://files.pythonhosted.org/packages/07/cf/bda0ae583758704719976b9ea69c8b089fa5f92e49683e517386539b21cf/lxml-6.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:16e5cbaa1a6351f2abefa4072e9aac1f09103b47fe7ab4496d54e5995b065162", size = 4735028, upload-time = "2026-04-09T14:37:03.602Z" }, - { url = "https://files.pythonhosted.org/packages/2f/0e/3bfb18778c6f73c7ead2d49a256501fa3052888b899826f5d1df1fbdf83b/lxml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:89f8746c206d8cf2c167221831645d6cc2b24464afd9c428a5eb3fd34c584eb1", size = 4969184, upload-time = "2026-04-09T14:37:05.914Z" }, - { url = "https://files.pythonhosted.org/packages/29/e6/796c77751a682d6d1bb9aa3fe43851b41a21b0377100e246a4a83a81d668/lxml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5d559a84b2fd583e5bcf8ec4af1ec895f98811684d5fbd6524ea31a04f92d4ad", size = 5103548, upload-time = "2026-04-09T14:37:08.605Z" }, - { url = "https://files.pythonhosted.org/packages/f9/5e/a02aee214f657f29d4690d88161de8ffb8f1b5139e792bae313b9479e317/lxml-6.0.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7966fbce2d18fde579d5593933d36ad98cc7c8dc7f2b1916d127057ce0415062", size = 5027775, upload-time = "2026-04-09T14:37:11.283Z" }, - { url = "https://files.pythonhosted.org/packages/20/e5/65dd25f2c366879d696d1c720af9a96fa0969d2d135a27b6140222fc6f68/lxml-6.0.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a1f258e6aa0e6eda2c1199f5582c062c96c7d4a28d96d0c4daa79e39b3f2a764", size = 5595348, upload-time = "2026-04-09T14:37:13.618Z" }, - { url = "https://files.pythonhosted.org/packages/f7/1f/2f0e80d7fd2ad9755d771af4ad46ea14bf871bc5a1d2d365a3f948940ddf/lxml-6.0.3-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:738aef404c862d2c3cd951364ee7175c9d50e8290f5726611c4208c0fba8d186", size = 5224217, upload-time = "2026-04-09T14:37:16.519Z" }, - { url = "https://files.pythonhosted.org/packages/3b/28/e1aaeee7d6a4c9f24a3e4535a4e19ce64b99eefbe7437d325b61623b1817/lxml-6.0.3-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:5c35e5c3ed300990a46a144d3514465713f812b35dacfa83e928c60db7c90af7", size = 5312245, upload-time = "2026-04-09T14:37:19.387Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ac/9633cb919124473e03c62862b0494bf0e1705f902fbd9627be4f648bddfb/lxml-6.0.3-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:4ff774b43712b0cf40d9888a5494ca39aefe990c946511cc947b9fddcf74a29b", size = 4637952, upload-time = "2026-04-09T14:37:21.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/aa/135baeea457d41989bafa78e437fe3a370c793aab0d8fb3da73ccae10095/lxml-6.0.3-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d20af2784c763928d0d0879cbc5a3739e4d81eefa0d68962d3478bff4c13e644", size = 5232782, upload-time = "2026-04-09T14:37:24.6Z" }, - { url = "https://files.pythonhosted.org/packages/0e/77/d05183ac8440cbc4c6fa386edb7ba9718bee4f097e58485b1cd1f9479d56/lxml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fdb7786ebefaa0dad0d399dfeaf146b370a14591af2f3aea59e06f931a426678", size = 5083889, upload-time = "2026-04-09T14:37:27.432Z" }, - { url = "https://files.pythonhosted.org/packages/6d/58/e9fda8fb82775491ad0290c7b17252f944b6c3a6974cd820d65910690351/lxml-6.0.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c71a387ea133481e725079cff22de45593bf0b834824de22829365ab1d2386c9", size = 4758658, upload-time = "2026-04-09T14:37:29.81Z" }, - { url = "https://files.pythonhosted.org/packages/8b/32/4aae9f004f79f9d200efd8343809cfe46077f8e5bd58f08708c320a20fcd/lxml-6.0.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:841b89fc3d910d61c7c267db6bb7dc3a8b3dac240edb66220fcdf96fe70a0552", size = 5619494, upload-time = "2026-04-09T14:37:33.482Z" }, - { url = "https://files.pythonhosted.org/packages/f9/49/407fa9e3c91e7c6d0762eaeedd50d4695bcd26db817e933ca689eb1f3df4/lxml-6.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:ac2d6cdafa29672d6a604c641bf67ace3fd0735ec6885501a94943379219ddbf", size = 5228386, upload-time = "2026-04-09T14:37:36.058Z" }, - { url = "https://files.pythonhosted.org/packages/99/92/39982f818acbb1dd67dd5d20c2a06bcb9f1f3b9a8ff0021e367904f82417/lxml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:609bf136a7339aeca2bd4268c7cd190f33d13118975fe9964eda8e5138f42802", size = 5247973, upload-time = "2026-04-09T14:37:38.836Z" }, - { url = "https://files.pythonhosted.org/packages/66/68/fcdbb78c8cda81a86e17b31abf103b7e474e474a09fb291a99e7a9b43eb8/lxml-6.0.3-cp314-cp314t-win32.whl", hash = "sha256:bf98f5f87f6484302e7cce4e2ca5af43562902852063d916c3e2f1c115fdce60", size = 3896249, upload-time = "2026-04-09T14:37:41.068Z" }, - { url = "https://files.pythonhosted.org/packages/88/fb/6292681ac4a4223b700569ce98f71662cb07c5a3ade4f346f5f0d5c574cf/lxml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:d3d65e511e4e656ec67b472110f7a72cbf8547ca15f76fe74cffa4e97412a064", size = 4391091, upload-time = "2026-04-09T14:37:43.357Z" }, - { url = "https://files.pythonhosted.org/packages/99/39/a0f486360a6f1b36fd2f5eb62d037652bef503d82b6f853aee6664cdfcac/lxml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:cbc7ce67f85b92db97c92219985432be84dc1ba9a028e68c6933e89551234df2", size = 3816374, upload-time = "2026-04-09T14:37:45.532Z" }, - { url = "https://files.pythonhosted.org/packages/a5/7c/3889981b55e83af1a710b2b54d40d5a9c7a2f7eab2e00cba6ba608fbdd22/lxml-6.0.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:093786037b934ef4747b0e8a0e1599fe7df7dd8246e7f07d43bba1c4c8bd7b84", size = 3929454, upload-time = "2026-04-09T14:38:54.873Z" }, - { url = "https://files.pythonhosted.org/packages/0b/29/a88dfb805c882b4fc81ef35d342629715a482037a0acd78ea8114e115d76/lxml-6.0.3-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6364aa77b13e04459df6a9d2b806465287e7540955527e75ebd5fda48532913d", size = 4209854, upload-time = "2026-04-09T14:38:57.541Z" }, - { url = "https://files.pythonhosted.org/packages/ca/01/44e71ace8c72bbb9aeb38551a4d314508133da88daf0dd9120a648af74ce/lxml-6.0.3-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:955550c78afb2be47755bd1b8153724292a5b539cf3f21665b310c145d08e6f8", size = 4317247, upload-time = "2026-04-09T14:38:59.977Z" }, - { url = "https://files.pythonhosted.org/packages/18/1a/ec02aafa56ff7675873e8fd4b6c7747aceaae037767434359e75d0b1075b/lxml-6.0.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a9a79144a8051bc5fbb223fac895b87eb67b361f27b00c2ed4a07ee34246b90", size = 4250372, upload-time = "2026-04-09T14:39:02.289Z" }, - { url = "https://files.pythonhosted.org/packages/35/13/94acd22f85e34e22eb984b4ac3db4c1b0c1e3daa0433dac5053fd26954d8/lxml-6.0.3-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8243937d4673b46da90b4f5ea2627fd26842225e62e885828fdb8133aa1f7b32", size = 4401010, upload-time = "2026-04-09T14:39:04.598Z" }, - { url = "https://files.pythonhosted.org/packages/28/7a/b3e8ed85413a4bd5c4850dfbd1eb18be7428127be0986f2a679d9d6098ad/lxml-6.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:5892d2ef99449ebd8e30544af5bc61fd9c30e9e989093a10589766422f6c5e1a", size = 3507669, upload-time = "2026-04-09T14:39:06.873Z" }, -] - -[[package]] -name = "lz4" -version = "4.4.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/51/f1b86d93029f418033dddf9b9f79c8d2641e7454080478ee2aab5123173e/lz4-4.4.5.tar.gz", hash = "sha256:5f0b9e53c1e82e88c10d7c180069363980136b9d7a8306c4dca4f760d60c39f0", size = 172886, upload-time = "2025-11-03T13:02:36.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/5b/6edcd23319d9e28b1bedf32768c3d1fd56eed8223960a2c47dacd2cec2af/lz4-4.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d6da84a26b3aa5da13a62e4b89ab36a396e9327de8cd48b436a3467077f8ccd4", size = 207391, upload-time = "2025-11-03T13:01:36.644Z" }, - { url = "https://files.pythonhosted.org/packages/34/36/5f9b772e85b3d5769367a79973b8030afad0d6b724444083bad09becd66f/lz4-4.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61d0ee03e6c616f4a8b69987d03d514e8896c8b1b7cc7598ad029e5c6aedfd43", size = 207146, upload-time = "2025-11-03T13:01:37.928Z" }, - { url = "https://files.pythonhosted.org/packages/04/f4/f66da5647c0d72592081a37c8775feacc3d14d2625bbdaabd6307c274565/lz4-4.4.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:33dd86cea8375d8e5dd001e41f321d0a4b1eb7985f39be1b6a4f466cd480b8a7", size = 1292623, upload-time = "2025-11-03T13:01:39.341Z" }, - { url = "https://files.pythonhosted.org/packages/85/fc/5df0f17467cdda0cad464a9197a447027879197761b55faad7ca29c29a04/lz4-4.4.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:609a69c68e7cfcfa9d894dc06be13f2e00761485b62df4e2472f1b66f7b405fb", size = 1279982, upload-time = "2025-11-03T13:01:40.816Z" }, - { url = "https://files.pythonhosted.org/packages/25/3b/b55cb577aa148ed4e383e9700c36f70b651cd434e1c07568f0a86c9d5fbb/lz4-4.4.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:75419bb1a559af00250b8f1360d508444e80ed4b26d9d40ec5b09fe7875cb989", size = 1368674, upload-time = "2025-11-03T13:01:42.118Z" }, - { url = "https://files.pythonhosted.org/packages/fb/31/e97e8c74c59ea479598e5c55cbe0b1334f03ee74ca97726e872944ed42df/lz4-4.4.5-cp311-cp311-win32.whl", hash = "sha256:12233624f1bc2cebc414f9efb3113a03e89acce3ab6f72035577bc61b270d24d", size = 88168, upload-time = "2025-11-03T13:01:43.282Z" }, - { url = "https://files.pythonhosted.org/packages/18/47/715865a6c7071f417bef9b57c8644f29cb7a55b77742bd5d93a609274e7e/lz4-4.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:8a842ead8ca7c0ee2f396ca5d878c4c40439a527ebad2b996b0444f0074ed004", size = 99491, upload-time = "2025-11-03T13:01:44.167Z" }, - { url = "https://files.pythonhosted.org/packages/14/e7/ac120c2ca8caec5c945e6356ada2aa5cfabd83a01e3170f264a5c42c8231/lz4-4.4.5-cp311-cp311-win_arm64.whl", hash = "sha256:83bc23ef65b6ae44f3287c38cbf82c269e2e96a26e560aa551735883388dcc4b", size = 91271, upload-time = "2025-11-03T13:01:45.016Z" }, - { url = "https://files.pythonhosted.org/packages/1b/ac/016e4f6de37d806f7cc8f13add0a46c9a7cfc41a5ddc2bc831d7954cf1ce/lz4-4.4.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:df5aa4cead2044bab83e0ebae56e0944cc7fcc1505c7787e9e1057d6d549897e", size = 207163, upload-time = "2025-11-03T13:01:45.895Z" }, - { url = "https://files.pythonhosted.org/packages/8d/df/0fadac6e5bd31b6f34a1a8dbd4db6a7606e70715387c27368586455b7fc9/lz4-4.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d0bf51e7745484d2092b3a51ae6eb58c3bd3ce0300cf2b2c14f76c536d5697a", size = 207150, upload-time = "2025-11-03T13:01:47.205Z" }, - { url = "https://files.pythonhosted.org/packages/b7/17/34e36cc49bb16ca73fb57fbd4c5eaa61760c6b64bce91fcb4e0f4a97f852/lz4-4.4.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7b62f94b523c251cf32aa4ab555f14d39bd1a9df385b72443fd76d7c7fb051f5", size = 1292045, upload-time = "2025-11-03T13:01:48.667Z" }, - { url = "https://files.pythonhosted.org/packages/90/1c/b1d8e3741e9fc89ed3b5f7ef5f22586c07ed6bb04e8343c2e98f0fa7ff04/lz4-4.4.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c3ea562c3af274264444819ae9b14dbbf1ab070aff214a05e97db6896c7597e", size = 1279546, upload-time = "2025-11-03T13:01:50.159Z" }, - { url = "https://files.pythonhosted.org/packages/55/d9/e3867222474f6c1b76e89f3bd914595af69f55bf2c1866e984c548afdc15/lz4-4.4.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24092635f47538b392c4eaeff14c7270d2c8e806bf4be2a6446a378591c5e69e", size = 1368249, upload-time = "2025-11-03T13:01:51.273Z" }, - { url = "https://files.pythonhosted.org/packages/b2/e7/d667d337367686311c38b580d1ca3d5a23a6617e129f26becd4f5dc458df/lz4-4.4.5-cp312-cp312-win32.whl", hash = "sha256:214e37cfe270948ea7eb777229e211c601a3e0875541c1035ab408fbceaddf50", size = 88189, upload-time = "2025-11-03T13:01:52.605Z" }, - { url = "https://files.pythonhosted.org/packages/a5/0b/a54cd7406995ab097fceb907c7eb13a6ddd49e0b231e448f1a81a50af65c/lz4-4.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:713a777de88a73425cf08eb11f742cd2c98628e79a8673d6a52e3c5f0c116f33", size = 99497, upload-time = "2025-11-03T13:01:53.477Z" }, - { url = "https://files.pythonhosted.org/packages/6a/7e/dc28a952e4bfa32ca16fa2eb026e7a6ce5d1411fcd5986cd08c74ec187b9/lz4-4.4.5-cp312-cp312-win_arm64.whl", hash = "sha256:a88cbb729cc333334ccfb52f070463c21560fca63afcf636a9f160a55fac3301", size = 91279, upload-time = "2025-11-03T13:01:54.419Z" }, - { url = "https://files.pythonhosted.org/packages/2f/46/08fd8ef19b782f301d56a9ccfd7dafec5fd4fc1a9f017cf22a1accb585d7/lz4-4.4.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6bb05416444fafea170b07181bc70640975ecc2a8c92b3b658c554119519716c", size = 207171, upload-time = "2025-11-03T13:01:56.595Z" }, - { url = "https://files.pythonhosted.org/packages/8f/3f/ea3334e59de30871d773963997ecdba96c4584c5f8007fd83cfc8f1ee935/lz4-4.4.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b424df1076e40d4e884cfcc4c77d815368b7fb9ebcd7e634f937725cd9a8a72a", size = 207163, upload-time = "2025-11-03T13:01:57.721Z" }, - { url = "https://files.pythonhosted.org/packages/41/7b/7b3a2a0feb998969f4793c650bb16eff5b06e80d1f7bff867feb332f2af2/lz4-4.4.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:216ca0c6c90719731c64f41cfbd6f27a736d7e50a10b70fad2a9c9b262ec923d", size = 1292136, upload-time = "2025-11-03T13:02:00.375Z" }, - { url = "https://files.pythonhosted.org/packages/89/d1/f1d259352227bb1c185288dd694121ea303e43404aa77560b879c90e7073/lz4-4.4.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:533298d208b58b651662dd972f52d807d48915176e5b032fb4f8c3b6f5fe535c", size = 1279639, upload-time = "2025-11-03T13:02:01.649Z" }, - { url = "https://files.pythonhosted.org/packages/d2/fb/ba9256c48266a09012ed1d9b0253b9aa4fe9cdff094f8febf5b26a4aa2a2/lz4-4.4.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:451039b609b9a88a934800b5fc6ee401c89ad9c175abf2f4d9f8b2e4ef1afc64", size = 1368257, upload-time = "2025-11-03T13:02:03.35Z" }, - { url = "https://files.pythonhosted.org/packages/a5/6d/dee32a9430c8b0e01bbb4537573cabd00555827f1a0a42d4e24ca803935c/lz4-4.4.5-cp313-cp313-win32.whl", hash = "sha256:a5f197ffa6fc0e93207b0af71b302e0a2f6f29982e5de0fbda61606dd3a55832", size = 88191, upload-time = "2025-11-03T13:02:04.406Z" }, - { url = "https://files.pythonhosted.org/packages/18/e0/f06028aea741bbecb2a7e9648f4643235279a770c7ffaf70bd4860c73661/lz4-4.4.5-cp313-cp313-win_amd64.whl", hash = "sha256:da68497f78953017deb20edff0dba95641cc86e7423dfadf7c0264e1ac60dc22", size = 99502, upload-time = "2025-11-03T13:02:05.886Z" }, - { url = "https://files.pythonhosted.org/packages/61/72/5bef44afb303e56078676b9f2486f13173a3c1e7f17eaac1793538174817/lz4-4.4.5-cp313-cp313-win_arm64.whl", hash = "sha256:c1cfa663468a189dab510ab231aad030970593f997746d7a324d40104db0d0a9", size = 91285, upload-time = "2025-11-03T13:02:06.77Z" }, - { url = "https://files.pythonhosted.org/packages/49/55/6a5c2952971af73f15ed4ebfdd69774b454bd0dc905b289082ca8664fba1/lz4-4.4.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:67531da3b62f49c939e09d56492baf397175ff39926d0bd5bd2d191ac2bff95f", size = 207348, upload-time = "2025-11-03T13:02:08.117Z" }, - { url = "https://files.pythonhosted.org/packages/4e/d7/fd62cbdbdccc35341e83aabdb3f6d5c19be2687d0a4eaf6457ddf53bba64/lz4-4.4.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a1acbbba9edbcbb982bc2cac5e7108f0f553aebac1040fbec67a011a45afa1ba", size = 207340, upload-time = "2025-11-03T13:02:09.152Z" }, - { url = "https://files.pythonhosted.org/packages/77/69/225ffadaacb4b0e0eb5fd263541edd938f16cd21fe1eae3cd6d5b6a259dc/lz4-4.4.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a482eecc0b7829c89b498fda883dbd50e98153a116de612ee7c111c8bcf82d1d", size = 1293398, upload-time = "2025-11-03T13:02:10.272Z" }, - { url = "https://files.pythonhosted.org/packages/c6/9e/2ce59ba4a21ea5dc43460cba6f34584e187328019abc0e66698f2b66c881/lz4-4.4.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e099ddfaa88f59dd8d36c8a3c66bd982b4984edf127eb18e30bb49bdba68ce67", size = 1281209, upload-time = "2025-11-03T13:02:12.091Z" }, - { url = "https://files.pythonhosted.org/packages/80/4f/4d946bd1624ec229b386a3bc8e7a85fa9a963d67d0a62043f0af0978d3da/lz4-4.4.5-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2af2897333b421360fdcce895c6f6281dc3fab018d19d341cf64d043fc8d90d", size = 1369406, upload-time = "2025-11-03T13:02:13.683Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/d429ba4720a9064722698b4b754fb93e42e625f1318b8fe834086c7c783b/lz4-4.4.5-cp313-cp313t-win32.whl", hash = "sha256:66c5de72bf4988e1b284ebdd6524c4bead2c507a2d7f172201572bac6f593901", size = 88325, upload-time = "2025-11-03T13:02:14.743Z" }, - { url = "https://files.pythonhosted.org/packages/4b/85/7ba10c9b97c06af6c8f7032ec942ff127558863df52d866019ce9d2425cf/lz4-4.4.5-cp313-cp313t-win_amd64.whl", hash = "sha256:cdd4bdcbaf35056086d910d219106f6a04e1ab0daa40ec0eeef1626c27d0fddb", size = 99643, upload-time = "2025-11-03T13:02:15.978Z" }, - { url = "https://files.pythonhosted.org/packages/77/4d/a175459fb29f909e13e57c8f475181ad8085d8d7869bd8ad99033e3ee5fa/lz4-4.4.5-cp313-cp313t-win_arm64.whl", hash = "sha256:28ccaeb7c5222454cd5f60fcd152564205bcb801bd80e125949d2dfbadc76bbd", size = 91504, upload-time = "2025-11-03T13:02:17.313Z" }, - { url = "https://files.pythonhosted.org/packages/63/9c/70bdbdb9f54053a308b200b4678afd13efd0eafb6ddcbb7f00077213c2e5/lz4-4.4.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c216b6d5275fc060c6280936bb3bb0e0be6126afb08abccde27eed23dead135f", size = 207586, upload-time = "2025-11-03T13:02:18.263Z" }, - { url = "https://files.pythonhosted.org/packages/b6/cb/bfead8f437741ce51e14b3c7d404e3a1f6b409c440bad9b8f3945d4c40a7/lz4-4.4.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c8e71b14938082ebaf78144f3b3917ac715f72d14c076f384a4c062df96f9df6", size = 207161, upload-time = "2025-11-03T13:02:19.286Z" }, - { url = "https://files.pythonhosted.org/packages/e7/18/b192b2ce465dfbeabc4fc957ece7a1d34aded0d95a588862f1c8a86ac448/lz4-4.4.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9b5e6abca8df9f9bdc5c3085f33ff32cdc86ed04c65e0355506d46a5ac19b6e9", size = 1292415, upload-time = "2025-11-03T13:02:20.829Z" }, - { url = "https://files.pythonhosted.org/packages/67/79/a4e91872ab60f5e89bfad3e996ea7dc74a30f27253faf95865771225ccba/lz4-4.4.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b84a42da86e8ad8537aabef062e7f661f4a877d1c74d65606c49d835d36d668", size = 1279920, upload-time = "2025-11-03T13:02:22.013Z" }, - { url = "https://files.pythonhosted.org/packages/f1/01/d52c7b11eaa286d49dae619c0eec4aabc0bf3cda7a7467eb77c62c4471f3/lz4-4.4.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bba042ec5a61fa77c7e380351a61cb768277801240249841defd2ff0a10742f", size = 1368661, upload-time = "2025-11-03T13:02:23.208Z" }, - { url = "https://files.pythonhosted.org/packages/f7/da/137ddeea14c2cb86864838277b2607d09f8253f152156a07f84e11768a28/lz4-4.4.5-cp314-cp314-win32.whl", hash = "sha256:bd85d118316b53ed73956435bee1997bd06cc66dd2fa74073e3b1322bd520a67", size = 90139, upload-time = "2025-11-03T13:02:24.301Z" }, - { url = "https://files.pythonhosted.org/packages/18/2c/8332080fd293f8337779a440b3a143f85e374311705d243439a3349b81ad/lz4-4.4.5-cp314-cp314-win_amd64.whl", hash = "sha256:92159782a4502858a21e0079d77cdcaade23e8a5d252ddf46b0652604300d7be", size = 101497, upload-time = "2025-11-03T13:02:25.187Z" }, - { url = "https://files.pythonhosted.org/packages/ca/28/2635a8141c9a4f4bc23f5135a92bbcf48d928d8ca094088c962df1879d64/lz4-4.4.5-cp314-cp314-win_arm64.whl", hash = "sha256:d994b87abaa7a88ceb7a37c90f547b8284ff9da694e6afcfaa8568d739faf3f7", size = 93812, upload-time = "2025-11-03T13:02:26.133Z" }, -] - -[[package]] -name = "mariadb" -version = "1.1.14" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c4/ba/cedef19833be88e07bfff11964441cda8a998f1628dd3b2fa3e7751d36e0/mariadb-1.1.14.tar.gz", hash = "sha256:e6d702a53eccf20922e47f2f45cfb5c7a0c2c6c0a46e4ee2d8a80d0ff4a52f34", size = 111715, upload-time = "2025-10-07T06:45:48.017Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/aa/a7b3c66b2792e8319ec9157d63851ff2e0b26496a05044e22b50a012a05e/mariadb-1.1.14-cp311-cp311-win32.whl", hash = "sha256:932a95016b7e9b8d78893aa5ee608e74199e3c6dd607dbe5e4da2010a4f67b88", size = 185061, upload-time = "2025-10-07T06:45:29.964Z" }, - { url = "https://files.pythonhosted.org/packages/54/04/ea2374867756b4082764484bc8b82e1798d94f171bcc914e08c60d640f8f/mariadb-1.1.14-cp311-cp311-win_amd64.whl", hash = "sha256:55ddbe5272c292cbcb2968d87681b5d2b327e65646a015e324b8eeb804d14531", size = 202016, upload-time = "2025-10-07T06:45:32.151Z" }, - { url = "https://files.pythonhosted.org/packages/00/04/659a8d30513700b5921ec96bddc07f550016c045fcbeb199d8cd18476ecc/mariadb-1.1.14-cp312-cp312-win32.whl", hash = "sha256:98d552a8bb599eceaa88f65002ad00bd88aeed160592c273a7e5c1d79ab733dd", size = 185266, upload-time = "2025-10-07T06:45:34.164Z" }, - { url = "https://files.pythonhosted.org/packages/e4/a9/8f210291bc5fc044e20497454f40d35b3bab326e2cab6fccdc38121cb2c1/mariadb-1.1.14-cp312-cp312-win_amd64.whl", hash = "sha256:685a1ad2a24fd0aae1c4416fe0ac794adc84ab9209c8d0c57078f770d39731db", size = 202112, upload-time = "2025-10-07T06:45:35.824Z" }, - { url = "https://files.pythonhosted.org/packages/f3/42/51130048bcce038bb859978250515f1aad90e9c4d273630a704e0a8b1ae1/mariadb-1.1.14-cp313-cp313-win32.whl", hash = "sha256:3d2c795cde606f4e12c0d73282b062433f414cae035675b0d81f2d65c9b79ac5", size = 185221, upload-time = "2025-10-07T06:45:37.848Z" }, - { url = "https://files.pythonhosted.org/packages/af/23/e952a7e442913abd8079cd27b80b69474895c93b3727fad41c7642a80c62/mariadb-1.1.14-cp313-cp313-win_amd64.whl", hash = "sha256:7fd603c5cf23c47ef0d28fdc2b4b79919ee7f75d00ed070d3cd1054dcf816aeb", size = 202121, upload-time = "2025-10-07T06:45:39.453Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f2/7059f83543a4264b98777d7cc8aa203e1ca6a13a461f730d1c97f29628d4/mariadb-1.1.14-cp314-cp314-win32.whl", hash = "sha256:1a50b4612c0dd5b69690cebb34cef552a7f64dcadeb5aa91d70cd99bf01bc5b3", size = 190620, upload-time = "2025-10-07T06:45:41.349Z" }, - { url = "https://files.pythonhosted.org/packages/f8/7c/7e094b0b396d742494f6346f2ffa9709e429970b0461aca50526f5f02f12/mariadb-1.1.14-cp314-cp314-win_amd64.whl", hash = "sha256:6659725837e48fa6af05e20128fb525029f706f1921d5dbf639a25b2f80b9f93", size = 206263, upload-time = "2025-10-07T06:45:43.227Z" }, -] - -[[package]] -name = "markdown" -version = "3.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, -] - -[[package]] -name = "markdown-it-py" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, -] - -[[package]] -name = "markdownlit" -version = "0.0.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "favicon" }, - { name = "htbuilder" }, - { name = "lxml" }, - { name = "markdown" }, - { name = "pymdown-extensions" }, - { name = "streamlit" }, - { name = "streamlit-extras" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/13/ab/c6eca20098fc7464df4f1626c65ca07b76903c5e2e9fa60763037e3554a4/markdownlit-0.0.7.tar.gz", hash = "sha256:553e2db454e2be4567caebef5176c98a40a7e24f7ea9c2fe8a1f05c1d9ea4005", size = 14289, upload-time = "2023-04-17T21:19:21.399Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/95/07e666469473043e16f263592c42e966e8b9a61fcc785ed39f48751adeb0/markdownlit-0.0.7-py3-none-any.whl", hash = "sha256:b58bb539dcb52e0b040ab2fed32f1f3146cbb2746dc3812940d9dd359c378bb6", size = 15266, upload-time = "2023-04-17T21:19:18.229Z" }, -] - -[[package]] -name = "markupsafe" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, - { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, - { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, - { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, - { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, - { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, - { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, - { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, - { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, - { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, - { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, - { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, - { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, - { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, - { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, - { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, - { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, - { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, - { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, - { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, - { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, - { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, - { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, - { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, - { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, - { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, - { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, - { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, - { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, - { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, - { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, - { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, - { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, - { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, -] - -[[package]] -name = "marshmallow" -version = "4.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/7e/1dbd4096eb7c148cd2841841916f78820bb85a4d80a0c25c02d30815a7fb/marshmallow-4.3.0.tar.gz", hash = "sha256:fb43c53b3fe240b8f6af37223d6ef1636f927ad9bea8ab323afad95dff090880", size = 224485, upload-time = "2026-04-03T21:46:32.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/e0/ff24e25218bb59eb6290a530cea40651b14068b6e3659b20f9c175179632/marshmallow-4.3.0-py3-none-any.whl", hash = "sha256:46c4fe6984707e3cbd485dfebbf0a59874f58d695aad05c1668d15e8c6e13b46", size = 49148, upload-time = "2026-04-03T21:46:31.241Z" }, -] - -[[package]] -name = "matplotlib" -version = "3.10.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "contourpy" }, - { name = "cycler" }, - { name = "fonttools" }, - { name = "kiwisolver" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pillow" }, - { name = "pyparsing" }, - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/86/de7e3a1cdcfc941483af70609edc06b83e7c8a0e0dc9ac325200a3f4d220/matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6be43b667360fef5c754dda5d25a32e6307a03c204f3c0fc5468b78fa87b4160", size = 8251215, upload-time = "2025-12-10T22:55:16.175Z" }, - { url = "https://files.pythonhosted.org/packages/fd/14/baad3222f424b19ce6ad243c71de1ad9ec6b2e4eb1e458a48fdc6d120401/matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2b336e2d91a3d7006864e0990c83b216fcdca64b5a6484912902cef87313d78", size = 8139625, upload-time = "2025-12-10T22:55:17.712Z" }, - { url = "https://files.pythonhosted.org/packages/8f/a0/7024215e95d456de5883e6732e708d8187d9753a21d32f8ddb3befc0c445/matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efb30e3baaea72ce5928e32bab719ab4770099079d66726a62b11b1ef7273be4", size = 8712614, upload-time = "2025-12-10T22:55:20.8Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f4/b8347351da9a5b3f41e26cf547252d861f685c6867d179a7c9d60ad50189/matplotlib-3.10.8-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d56a1efd5bfd61486c8bc968fa18734464556f0fb8e51690f4ac25d85cbbbbc2", size = 9540997, upload-time = "2025-12-10T22:55:23.258Z" }, - { url = "https://files.pythonhosted.org/packages/9e/c0/c7b914e297efe0bc36917bf216b2acb91044b91e930e878ae12981e461e5/matplotlib-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238b7ce5717600615c895050239ec955d91f321c209dd110db988500558e70d6", size = 9596825, upload-time = "2025-12-10T22:55:25.217Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d3/a4bbc01c237ab710a1f22b4da72f4ff6d77eb4c7735ea9811a94ae239067/matplotlib-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:18821ace09c763ec93aef5eeff087ee493a24051936d7b9ebcad9662f66501f9", size = 8135090, upload-time = "2025-12-10T22:55:27.162Z" }, - { url = "https://files.pythonhosted.org/packages/89/dd/a0b6588f102beab33ca6f5218b31725216577b2a24172f327eaf6417d5c9/matplotlib-3.10.8-cp311-cp311-win_arm64.whl", hash = "sha256:bab485bcf8b1c7d2060b4fcb6fc368a9e6f4cd754c9c2fea281f4be21df394a2", size = 8012377, upload-time = "2025-12-10T22:55:29.185Z" }, - { url = "https://files.pythonhosted.org/packages/9e/67/f997cdcbb514012eb0d10cd2b4b332667997fb5ebe26b8d41d04962fa0e6/matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a", size = 8260453, upload-time = "2025-12-10T22:55:30.709Z" }, - { url = "https://files.pythonhosted.org/packages/7e/65/07d5f5c7f7c994f12c768708bd2e17a4f01a2b0f44a1c9eccad872433e2e/matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58", size = 8148321, upload-time = "2025-12-10T22:55:33.265Z" }, - { url = "https://files.pythonhosted.org/packages/3e/f3/c5195b1ae57ef85339fd7285dfb603b22c8b4e79114bae5f4f0fcf688677/matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04", size = 8716944, upload-time = "2025-12-10T22:55:34.922Z" }, - { url = "https://files.pythonhosted.org/packages/00/f9/7638f5cc82ec8a7aa005de48622eecc3ed7c9854b96ba15bd76b7fd27574/matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f", size = 9550099, upload-time = "2025-12-10T22:55:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/57/61/78cd5920d35b29fd2a0fe894de8adf672ff52939d2e9b43cb83cd5ce1bc7/matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466", size = 9613040, upload-time = "2025-12-10T22:55:38.715Z" }, - { url = "https://files.pythonhosted.org/packages/30/4e/c10f171b6e2f44d9e3a2b96efa38b1677439d79c99357600a62cc1e9594e/matplotlib-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf", size = 8142717, upload-time = "2025-12-10T22:55:41.103Z" }, - { url = "https://files.pythonhosted.org/packages/f1/76/934db220026b5fef85f45d51a738b91dea7d70207581063cd9bd8fafcf74/matplotlib-3.10.8-cp312-cp312-win_arm64.whl", hash = "sha256:3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b", size = 8012751, upload-time = "2025-12-10T22:55:42.684Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6", size = 8261076, upload-time = "2025-12-10T22:55:44.648Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1", size = 8148794, upload-time = "2025-12-10T22:55:46.252Z" }, - { url = "https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486", size = 8718474, upload-time = "2025-12-10T22:55:47.864Z" }, - { url = "https://files.pythonhosted.org/packages/01/be/cd478f4b66f48256f42927d0acbcd63a26a893136456cd079c0cc24fbabf/matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce", size = 9549637, upload-time = "2025-12-10T22:55:50.048Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7c/8dc289776eae5109e268c4fb92baf870678dc048a25d4ac903683b86d5bf/matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6", size = 9613678, upload-time = "2025-12-10T22:55:52.21Z" }, - { url = "https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149", size = 8142686, upload-time = "2025-12-10T22:55:54.253Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/8d8a8730e968185514680c2a6625943f70269509c3dcfc0dcf7d75928cb8/matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645", size = 8012917, upload-time = "2025-12-10T22:55:56.268Z" }, - { url = "https://files.pythonhosted.org/packages/b5/27/51fe26e1062f298af5ef66343d8ef460e090a27fea73036c76c35821df04/matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077", size = 8305679, upload-time = "2025-12-10T22:55:57.856Z" }, - { url = "https://files.pythonhosted.org/packages/2c/1e/4de865bc591ac8e3062e835f42dd7fe7a93168d519557837f0e37513f629/matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22", size = 8198336, upload-time = "2025-12-10T22:55:59.371Z" }, - { url = "https://files.pythonhosted.org/packages/c6/cb/2f7b6e75fb4dce87ef91f60cac4f6e34f4c145ab036a22318ec837971300/matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39", size = 8731653, upload-time = "2025-12-10T22:56:01.032Z" }, - { url = "https://files.pythonhosted.org/packages/46/b3/bd9c57d6ba670a37ab31fb87ec3e8691b947134b201f881665b28cc039ff/matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565", size = 9561356, upload-time = "2025-12-10T22:56:02.95Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3d/8b94a481456dfc9dfe6e39e93b5ab376e50998cddfd23f4ae3b431708f16/matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a", size = 9614000, upload-time = "2025-12-10T22:56:05.411Z" }, - { url = "https://files.pythonhosted.org/packages/bd/cd/bc06149fe5585ba800b189a6a654a75f1f127e8aab02fd2be10df7fa500c/matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958", size = 8220043, upload-time = "2025-12-10T22:56:07.551Z" }, - { url = "https://files.pythonhosted.org/packages/e3/de/b22cf255abec916562cc04eef457c13e58a1990048de0c0c3604d082355e/matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5", size = 8062075, upload-time = "2025-12-10T22:56:09.178Z" }, - { url = "https://files.pythonhosted.org/packages/3c/43/9c0ff7a2f11615e516c3b058e1e6e8f9614ddeca53faca06da267c48345d/matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f", size = 8262481, upload-time = "2025-12-10T22:56:10.885Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ca/e8ae28649fcdf039fda5ef554b40a95f50592a3c47e6f7270c9561c12b07/matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b", size = 8151473, upload-time = "2025-12-10T22:56:12.377Z" }, - { url = "https://files.pythonhosted.org/packages/f1/6f/009d129ae70b75e88cbe7e503a12a4c0670e08ed748a902c2568909e9eb5/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d", size = 9553896, upload-time = "2025-12-10T22:56:14.432Z" }, - { url = "https://files.pythonhosted.org/packages/f5/26/4221a741eb97967bc1fd5e4c52b9aa5a91b2f4ec05b59f6def4d820f9df9/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008", size = 9824193, upload-time = "2025-12-10T22:56:16.29Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/3abf75f38605772cf48a9daf5821cd4f563472f38b4b828c6fba6fa6d06e/matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c", size = 9615444, upload-time = "2025-12-10T22:56:18.155Z" }, - { url = "https://files.pythonhosted.org/packages/93/a5/de89ac80f10b8dc615807ee1133cd99ac74082581196d4d9590bea10690d/matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11", size = 8272719, upload-time = "2025-12-10T22:56:20.366Z" }, - { url = "https://files.pythonhosted.org/packages/69/ce/b006495c19ccc0a137b48083168a37bd056392dee02f87dba0472f2797fe/matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8", size = 8144205, upload-time = "2025-12-10T22:56:22.239Z" }, - { url = "https://files.pythonhosted.org/packages/68/d9/b31116a3a855bd313c6fcdb7226926d59b041f26061c6c5b1be66a08c826/matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50", size = 8305785, upload-time = "2025-12-10T22:56:24.218Z" }, - { url = "https://files.pythonhosted.org/packages/1e/90/6effe8103f0272685767ba5f094f453784057072f49b393e3ea178fe70a5/matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908", size = 8198361, upload-time = "2025-12-10T22:56:26.787Z" }, - { url = "https://files.pythonhosted.org/packages/d7/65/a73188711bea603615fc0baecca1061429ac16940e2385433cc778a9d8e7/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a", size = 9561357, upload-time = "2025-12-10T22:56:28.953Z" }, - { url = "https://files.pythonhosted.org/packages/f4/3d/b5c5d5d5be8ce63292567f0e2c43dde9953d3ed86ac2de0a72e93c8f07a1/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1", size = 9823610, upload-time = "2025-12-10T22:56:31.455Z" }, - { url = "https://files.pythonhosted.org/packages/4d/4b/e7beb6bbd49f6bae727a12b270a2654d13c397576d25bd6786e47033300f/matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c", size = 9614011, upload-time = "2025-12-10T22:56:33.85Z" }, - { url = "https://files.pythonhosted.org/packages/7c/e6/76f2813d31f032e65f6f797e3f2f6e4aab95b65015924b1c51370395c28a/matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b", size = 8362801, upload-time = "2025-12-10T22:56:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" }, - { url = "https://files.pythonhosted.org/packages/04/30/3afaa31c757f34b7725ab9d2ba8b48b5e89c2019c003e7d0ead143aabc5a/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6da7c2ce169267d0d066adcf63758f0604aa6c3eebf67458930f9d9b79ad1db1", size = 8249198, upload-time = "2025-12-10T22:56:45.584Z" }, - { url = "https://files.pythonhosted.org/packages/48/2f/6334aec331f57485a642a7c8be03cb286f29111ae71c46c38b363230063c/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9153c3292705be9f9c64498a8872118540c3f4123d1a1c840172edf262c8be4a", size = 8136817, upload-time = "2025-12-10T22:56:47.339Z" }, - { url = "https://files.pythonhosted.org/packages/73/e4/6d6f14b2a759c622f191b2d67e9075a3f56aaccb3be4bb9bb6890030d0a0/matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2", size = 8713867, upload-time = "2025-12-10T22:56:48.954Z" }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, -] - -[[package]] -name = "memorydb" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/3a/ca5bb15f53d1edc40b2662f49c84dcf06e4dfe38a0c9ad64dac1a96cebc2/memorydb-1.2.1.tar.gz", hash = "sha256:cfb70f0e289e10c6b8f0d932da9a1a656b074054338c9d0f4651a6f58ff4ef96", size = 26598, upload-time = "2017-08-09T21:23:53.668Z" } - -[[package]] -name = "milvus-lite" -version = "2.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tqdm" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/b2/acc5024c8e8b6a0b034670b8e8af306ebd633ede777dcbf557eac4785937/milvus_lite-2.5.1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:6b014453200ba977be37ba660cb2d021030375fa6a35bc53c2e1d92980a0c512", size = 27934713, upload-time = "2025-06-30T04:23:37.028Z" }, - { url = "https://files.pythonhosted.org/packages/9b/2e/746f5bb1d6facd1e73eb4af6dd5efda11125b0f29d7908a097485ca6cad9/milvus_lite-2.5.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a2e031088bf308afe5f8567850412d618cfb05a65238ed1a6117f60decccc95a", size = 24421451, upload-time = "2025-06-30T04:23:51.747Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cf/3d1fee5c16c7661cf53977067a34820f7269ed8ba99fe9cf35efc1700866/milvus_lite-2.5.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:a13277e9bacc6933dea172e42231f7e6135bd3bdb073dd2688ee180418abd8d9", size = 45337093, upload-time = "2025-06-30T04:24:06.706Z" }, - { url = "https://files.pythonhosted.org/packages/d3/82/41d9b80f09b82e066894d9b508af07b7b0fa325ce0322980674de49106a0/milvus_lite-2.5.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25ce13f4b8d46876dd2b7ac8563d7d8306da7ff3999bb0d14b116b30f71d706c", size = 55263911, upload-time = "2025-06-30T04:24:19.434Z" }, -] - -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, -] - -[[package]] -name = "msgpack" -version = "1.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4d/f2/bfb55a6236ed8725a96b0aa3acbd0ec17588e6a2c3b62a93eb513ed8783f/msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e", size = 173581, upload-time = "2025-10-08T09:15:56.596Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/97/560d11202bcd537abca693fd85d81cebe2107ba17301de42b01ac1677b69/msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e86a607e558d22985d856948c12a3fa7b42efad264dca8a3ebbcfa2735d786c", size = 82271, upload-time = "2025-10-08T09:14:49.967Z" }, - { url = "https://files.pythonhosted.org/packages/83/04/28a41024ccbd67467380b6fb440ae916c1e4f25e2cd4c63abe6835ac566e/msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:283ae72fc89da59aa004ba147e8fc2f766647b1251500182fac0350d8af299c0", size = 84914, upload-time = "2025-10-08T09:14:50.958Z" }, - { url = "https://files.pythonhosted.org/packages/71/46/b817349db6886d79e57a966346cf0902a426375aadc1e8e7a86a75e22f19/msgpack-1.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61c8aa3bd513d87c72ed0b37b53dd5c5a0f58f2ff9f26e1555d3bd7948fb7296", size = 416962, upload-time = "2025-10-08T09:14:51.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/e0/6cc2e852837cd6086fe7d8406af4294e66827a60a4cf60b86575a4a65ca8/msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:454e29e186285d2ebe65be34629fa0e8605202c60fbc7c4c650ccd41870896ef", size = 426183, upload-time = "2025-10-08T09:14:53.477Z" }, - { url = "https://files.pythonhosted.org/packages/25/98/6a19f030b3d2ea906696cedd1eb251708e50a5891d0978b012cb6107234c/msgpack-1.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7bc8813f88417599564fafa59fd6f95be417179f76b40325b500b3c98409757c", size = 411454, upload-time = "2025-10-08T09:14:54.648Z" }, - { url = "https://files.pythonhosted.org/packages/b7/cd/9098fcb6adb32187a70b7ecaabf6339da50553351558f37600e53a4a2a23/msgpack-1.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bafca952dc13907bdfdedfc6a5f579bf4f292bdd506fadb38389afa3ac5b208e", size = 422341, upload-time = "2025-10-08T09:14:56.328Z" }, - { url = "https://files.pythonhosted.org/packages/e6/ae/270cecbcf36c1dc85ec086b33a51a4d7d08fc4f404bdbc15b582255d05ff/msgpack-1.1.2-cp311-cp311-win32.whl", hash = "sha256:602b6740e95ffc55bfb078172d279de3773d7b7db1f703b2f1323566b878b90e", size = 64747, upload-time = "2025-10-08T09:14:57.882Z" }, - { url = "https://files.pythonhosted.org/packages/2a/79/309d0e637f6f37e83c711f547308b91af02b72d2326ddd860b966080ef29/msgpack-1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68", size = 71633, upload-time = "2025-10-08T09:14:59.177Z" }, - { url = "https://files.pythonhosted.org/packages/73/4d/7c4e2b3d9b1106cd0aa6cb56cc57c6267f59fa8bfab7d91df5adc802c847/msgpack-1.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:86f8136dfa5c116365a8a651a7d7484b65b13339731dd6faebb9a0242151c406", size = 64755, upload-time = "2025-10-08T09:15:00.48Z" }, - { url = "https://files.pythonhosted.org/packages/ad/bd/8b0d01c756203fbab65d265859749860682ccd2a59594609aeec3a144efa/msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa", size = 81939, upload-time = "2025-10-08T09:15:01.472Z" }, - { url = "https://files.pythonhosted.org/packages/34/68/ba4f155f793a74c1483d4bdef136e1023f7bcba557f0db4ef3db3c665cf1/msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb", size = 85064, upload-time = "2025-10-08T09:15:03.764Z" }, - { url = "https://files.pythonhosted.org/packages/f2/60/a064b0345fc36c4c3d2c743c82d9100c40388d77f0b48b2f04d6041dbec1/msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f", size = 417131, upload-time = "2025-10-08T09:15:05.136Z" }, - { url = "https://files.pythonhosted.org/packages/65/92/a5100f7185a800a5d29f8d14041f61475b9de465ffcc0f3b9fba606e4505/msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42", size = 427556, upload-time = "2025-10-08T09:15:06.837Z" }, - { url = "https://files.pythonhosted.org/packages/f5/87/ffe21d1bf7d9991354ad93949286f643b2bb6ddbeab66373922b44c3b8cc/msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9", size = 404920, upload-time = "2025-10-08T09:15:08.179Z" }, - { url = "https://files.pythonhosted.org/packages/ff/41/8543ed2b8604f7c0d89ce066f42007faac1eaa7d79a81555f206a5cdb889/msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620", size = 415013, upload-time = "2025-10-08T09:15:09.83Z" }, - { url = "https://files.pythonhosted.org/packages/41/0d/2ddfaa8b7e1cee6c490d46cb0a39742b19e2481600a7a0e96537e9c22f43/msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029", size = 65096, upload-time = "2025-10-08T09:15:11.11Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ec/d431eb7941fb55a31dd6ca3404d41fbb52d99172df2e7707754488390910/msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b", size = 72708, upload-time = "2025-10-08T09:15:12.554Z" }, - { url = "https://files.pythonhosted.org/packages/c5/31/5b1a1f70eb0e87d1678e9624908f86317787b536060641d6798e3cf70ace/msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69", size = 64119, upload-time = "2025-10-08T09:15:13.589Z" }, - { url = "https://files.pythonhosted.org/packages/6b/31/b46518ecc604d7edf3a4f94cb3bf021fc62aa301f0cb849936968164ef23/msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf", size = 81212, upload-time = "2025-10-08T09:15:14.552Z" }, - { url = "https://files.pythonhosted.org/packages/92/dc/c385f38f2c2433333345a82926c6bfa5ecfff3ef787201614317b58dd8be/msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7", size = 84315, upload-time = "2025-10-08T09:15:15.543Z" }, - { url = "https://files.pythonhosted.org/packages/d3/68/93180dce57f684a61a88a45ed13047558ded2be46f03acb8dec6d7c513af/msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999", size = 412721, upload-time = "2025-10-08T09:15:16.567Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ba/459f18c16f2b3fc1a1ca871f72f07d70c07bf768ad0a507a698b8052ac58/msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e", size = 424657, upload-time = "2025-10-08T09:15:17.825Z" }, - { url = "https://files.pythonhosted.org/packages/38/f8/4398c46863b093252fe67368b44edc6c13b17f4e6b0e4929dbf0bdb13f23/msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162", size = 402668, upload-time = "2025-10-08T09:15:19.003Z" }, - { url = "https://files.pythonhosted.org/packages/28/ce/698c1eff75626e4124b4d78e21cca0b4cc90043afb80a507626ea354ab52/msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794", size = 419040, upload-time = "2025-10-08T09:15:20.183Z" }, - { url = "https://files.pythonhosted.org/packages/67/32/f3cd1667028424fa7001d82e10ee35386eea1408b93d399b09fb0aa7875f/msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c", size = 65037, upload-time = "2025-10-08T09:15:21.416Z" }, - { url = "https://files.pythonhosted.org/packages/74/07/1ed8277f8653c40ebc65985180b007879f6a836c525b3885dcc6448ae6cb/msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9", size = 72631, upload-time = "2025-10-08T09:15:22.431Z" }, - { url = "https://files.pythonhosted.org/packages/e5/db/0314e4e2db56ebcf450f277904ffd84a7988b9e5da8d0d61ab2d057df2b6/msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84", size = 64118, upload-time = "2025-10-08T09:15:23.402Z" }, - { url = "https://files.pythonhosted.org/packages/22/71/201105712d0a2ff07b7873ed3c220292fb2ea5120603c00c4b634bcdafb3/msgpack-1.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e23ce8d5f7aa6ea6d2a2b326b4ba46c985dbb204523759984430db7114f8aa00", size = 81127, upload-time = "2025-10-08T09:15:24.408Z" }, - { url = "https://files.pythonhosted.org/packages/1b/9f/38ff9e57a2eade7bf9dfee5eae17f39fc0e998658050279cbb14d97d36d9/msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c15b7d74c939ebe620dd8e559384be806204d73b4f9356320632d783d1f7939", size = 84981, upload-time = "2025-10-08T09:15:25.812Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a9/3536e385167b88c2cc8f4424c49e28d49a6fc35206d4a8060f136e71f94c/msgpack-1.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99e2cb7b9031568a2a5c73aa077180f93dd2e95b4f8d3b8e14a73ae94a9e667e", size = 411885, upload-time = "2025-10-08T09:15:27.22Z" }, - { url = "https://files.pythonhosted.org/packages/2f/40/dc34d1a8d5f1e51fc64640b62b191684da52ca469da9cd74e84936ffa4a6/msgpack-1.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:180759d89a057eab503cf62eeec0aa61c4ea1200dee709f3a8e9397dbb3b6931", size = 419658, upload-time = "2025-10-08T09:15:28.4Z" }, - { url = "https://files.pythonhosted.org/packages/3b/ef/2b92e286366500a09a67e03496ee8b8ba00562797a52f3c117aa2b29514b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:04fb995247a6e83830b62f0b07bf36540c213f6eac8e851166d8d86d83cbd014", size = 403290, upload-time = "2025-10-08T09:15:29.764Z" }, - { url = "https://files.pythonhosted.org/packages/78/90/e0ea7990abea5764e4655b8177aa7c63cdfa89945b6e7641055800f6c16b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e22ab046fa7ede9e36eeb4cfad44d46450f37bb05d5ec482b02868f451c95e2", size = 415234, upload-time = "2025-10-08T09:15:31.022Z" }, - { url = "https://files.pythonhosted.org/packages/72/4e/9390aed5db983a2310818cd7d3ec0aecad45e1f7007e0cda79c79507bb0d/msgpack-1.1.2-cp314-cp314-win32.whl", hash = "sha256:80a0ff7d4abf5fecb995fcf235d4064b9a9a8a40a3ab80999e6ac1e30b702717", size = 66391, upload-time = "2025-10-08T09:15:32.265Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f1/abd09c2ae91228c5f3998dbd7f41353def9eac64253de3c8105efa2082f7/msgpack-1.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:9ade919fac6a3e7260b7f64cea89df6bec59104987cbea34d34a2fa15d74310b", size = 73787, upload-time = "2025-10-08T09:15:33.219Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b0/9d9f667ab48b16ad4115c1935d94023b82b3198064cb84a123e97f7466c1/msgpack-1.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:59415c6076b1e30e563eb732e23b994a61c159cec44deaf584e5cc1dd662f2af", size = 66453, upload-time = "2025-10-08T09:15:34.225Z" }, - { url = "https://files.pythonhosted.org/packages/16/67/93f80545eb1792b61a217fa7f06d5e5cb9e0055bed867f43e2b8e012e137/msgpack-1.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:897c478140877e5307760b0ea66e0932738879e7aa68144d9b78ea4c8302a84a", size = 85264, upload-time = "2025-10-08T09:15:35.61Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/33c8a24959cf193966ef11a6f6a2995a65eb066bd681fd085afd519a57ce/msgpack-1.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a668204fa43e6d02f89dbe79a30b0d67238d9ec4c5bd8a940fc3a004a47b721b", size = 89076, upload-time = "2025-10-08T09:15:36.619Z" }, - { url = "https://files.pythonhosted.org/packages/fc/6b/62e85ff7193663fbea5c0254ef32f0c77134b4059f8da89b958beb7696f3/msgpack-1.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5559d03930d3aa0f3aacb4c42c776af1a2ace2611871c84a75afe436695e6245", size = 435242, upload-time = "2025-10-08T09:15:37.647Z" }, - { url = "https://files.pythonhosted.org/packages/c1/47/5c74ecb4cc277cf09f64e913947871682ffa82b3b93c8dad68083112f412/msgpack-1.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70c5a7a9fea7f036b716191c29047374c10721c389c21e9ffafad04df8c52c90", size = 432509, upload-time = "2025-10-08T09:15:38.794Z" }, - { url = "https://files.pythonhosted.org/packages/24/a4/e98ccdb56dc4e98c929a3f150de1799831c0a800583cde9fa022fa90602d/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2cb069d8b981abc72b41aea1c580ce92d57c673ec61af4c500153a626cb9e20", size = 415957, upload-time = "2025-10-08T09:15:40.238Z" }, - { url = "https://files.pythonhosted.org/packages/da/28/6951f7fb67bc0a4e184a6b38ab71a92d9ba58080b27a77d3e2fb0be5998f/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d62ce1f483f355f61adb5433ebfd8868c5f078d1a52d042b0a998682b4fa8c27", size = 422910, upload-time = "2025-10-08T09:15:41.505Z" }, - { url = "https://files.pythonhosted.org/packages/f0/03/42106dcded51f0a0b5284d3ce30a671e7bd3f7318d122b2ead66ad289fed/msgpack-1.1.2-cp314-cp314t-win32.whl", hash = "sha256:1d1418482b1ee984625d88aa9585db570180c286d942da463533b238b98b812b", size = 75197, upload-time = "2025-10-08T09:15:42.954Z" }, - { url = "https://files.pythonhosted.org/packages/15/86/d0071e94987f8db59d4eeb386ddc64d0bb9b10820a8d82bcd3e53eeb2da6/msgpack-1.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5a46bf7e831d09470ad92dff02b8b1ac92175ca36b087f904a0519857c6be3ff", size = 85772, upload-time = "2025-10-08T09:15:43.954Z" }, - { url = "https://files.pythonhosted.org/packages/81/f2/08ace4142eb281c12701fc3b93a10795e4d4dc7f753911d836675050f886/msgpack-1.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d99ef64f349d5ec3293688e91486c5fdb925ed03807f64d98d205d2713c60b46", size = 70868, upload-time = "2025-10-08T09:15:44.959Z" }, -] - -[[package]] -name = "multidict" -version = "6.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, - { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, - { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, - { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, - { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, - { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, - { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, - { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, - { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, - { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, - { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, - { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, - { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, - { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, - { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z" }, - { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z" }, - { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, - { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, - { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, - { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, - { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, - { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, - { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, - { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, - { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, - { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, - { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, - { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, - { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, - { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, - { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, - { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, - { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, - { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, - { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, - { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, - { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, - { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, - { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, - { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, - { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, - { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, - { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, - { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, - { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, - { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, - { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, - { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, - { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, - { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, - { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, - { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, - { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, - { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, - { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, - { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, - { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, - { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, - { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, - { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, - { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, - { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, - { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, - { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, - { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, - { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, - { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, - { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, - { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, - { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, - { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, - { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, - { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, - { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, - { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, - { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, - { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, - { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, - { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, - { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, - { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, - { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, - { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, -] - -[[package]] -name = "mysql-connector" -version = "2.2.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/28/04/e40098f3730e75bbe36a338926f566ea803550a34fb50535499f4fc4787a/mysql-connector-2.2.9.tar.gz", hash = "sha256:1733e6ce52a049243de3264f1fbc22a852cb35458c4ad739ba88189285efdf32", size = 11852649, upload-time = "2019-04-01T14:40:08.091Z" } - -[[package]] -name = "mysql-connector-python" -version = "9.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6e/c89babc7de3df01467d159854414659c885152579903a8220c8db02a3835/mysql_connector_python-9.6.0.tar.gz", hash = "sha256:c453bb55347174d87504b534246fb10c589daf5d057515bf615627198a3c7ef1", size = 12254999, upload-time = "2026-02-10T12:04:52.63Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/08/0e9bce000736454c2b8bb4c40bded79328887483689487dad7df4cf59fb7/mysql_connector_python-9.6.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:011931f7392a1087e10d305b0303f2a20cc1af2c1c8a15cd5691609aa95dfcbd", size = 17582646, upload-time = "2026-01-21T09:04:48.327Z" }, - { url = "https://files.pythonhosted.org/packages/93/aa/3dd4db039fc6a9bcbdbade83be9914ead6786c0be4918170dfaf89327b76/mysql_connector_python-9.6.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b5212372aff6833473d2560ac87d3df9fb2498d0faacb7ebf231d947175fa36a", size = 18449358, upload-time = "2026-01-21T09:04:50.278Z" }, - { url = "https://files.pythonhosted.org/packages/53/38/ecd6d35382b6265ff5f030464d53b45e51ff2c2523ab88771c277fd84c05/mysql_connector_python-9.6.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:61deca6e243fafbb3cf08ae27bd0c83d0f8188de8456e46aeba0d3db15bb7230", size = 34169309, upload-time = "2026-01-21T09:04:52.402Z" }, - { url = "https://files.pythonhosted.org/packages/18/1d/fe1133eb76089342854d8fbe88e28598f7e06bc684a763d21fc7b23f1d5e/mysql_connector_python-9.6.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:adabbc5e1475cdf5fb6f1902a25edc3bd1e0726fa45f01ab1b8f479ff43b3337", size = 34541101, upload-time = "2026-01-21T09:04:55.897Z" }, - { url = "https://files.pythonhosted.org/packages/3f/99/da0f55beb970ca049fd7d37a6391d686222af89a8b13e636d8e9bbd06536/mysql_connector_python-9.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:8732ca0b7417b45238bcbfc7e64d9c4d62c759672207c6284f0921c366efddc7", size = 16514767, upload-time = "2026-02-10T12:03:50.584Z" }, - { url = "https://files.pythonhosted.org/packages/8f/d9/2a4b4d90b52f4241f0f71618cd4bd8779dd6d18db8058b0a4dd83ec0541c/mysql_connector_python-9.6.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9664e217c72dd6fb700f4c8512af90261f72d2f5d7c00c4e13e4c1e09bfa3d5e", size = 17585672, upload-time = "2026-02-10T12:03:52.955Z" }, - { url = "https://files.pythonhosted.org/packages/33/91/2495835733a054e716a17dc28404748b33f2dc1da1ae4396fb45574adf40/mysql_connector_python-9.6.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:1ed4b5c4761e5333035293e746683890e4ef2e818e515d14023fd80293bc31fa", size = 18452624, upload-time = "2026-02-10T12:03:56.153Z" }, - { url = "https://files.pythonhosted.org/packages/7a/69/e83abbbbf7f8eed855b5a5ff7285bc0afb1199418ac036c7691edf41e154/mysql_connector_python-9.6.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5095758dcb89a6bce2379f349da336c268c407129002b595c5dba82ce387e2a5", size = 34169154, upload-time = "2026-02-10T12:03:58.831Z" }, - { url = "https://files.pythonhosted.org/packages/82/44/67bb61c71f398fbc739d07e8dcadad94e2f655874cb32ae851454066bea0/mysql_connector_python-9.6.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4ae4e7780fad950a4f267dea5851048d160f5b71314a342cdbf30b154f1c74f7", size = 34542947, upload-time = "2026-02-10T12:04:02.408Z" }, - { url = "https://files.pythonhosted.org/packages/ba/39/994c4f7e9c59d3ca534a831d18442ac4c529865db20aeaa4fd94e2af5efd/mysql_connector_python-9.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:c180e0b4100d7402e03993bfac5c97d18e01d7ca9d198d742fffc245077f8ffe", size = 16515709, upload-time = "2026-02-10T12:04:04.924Z" }, - { url = "https://files.pythonhosted.org/packages/2f/58/9521aa678708ec6cebfd40524c14c3d151e4f29e3774e6086aa0a30d203b/mysql_connector_python-9.6.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e86e45a7b540ca09af8a18ecfa761e0cdeccfdb62818331614ec030ae44bfd26", size = 17585837, upload-time = "2026-02-10T12:04:07.004Z" }, - { url = "https://files.pythonhosted.org/packages/39/8d/b108f9bcce9780f6a1f91decb2af54defdaf845e237ddc42f2b4578f1cd7/mysql_connector_python-9.6.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:8d3e9252384e1b7f95b07020664f2673d9c29c5e95eeda2e048b3331e190b9d4", size = 18452844, upload-time = "2026-02-10T12:04:09.418Z" }, - { url = "https://files.pythonhosted.org/packages/d6/28/735cd93d16e76dc2feb4abb3f1229a1d9475af34d80c26712fec6abe1d70/mysql_connector_python-9.6.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:0fa18ead33cb699ea92005695077cef09aa494eebf51164ee30c891c3eaea90c", size = 34169374, upload-time = "2026-02-10T12:04:12.13Z" }, - { url = "https://files.pythonhosted.org/packages/42/07/069983799cf4050c68f61a494f94b06f095fee6026ab0dd863a14de30867/mysql_connector_python-9.6.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:a26490cb029bf7b18a1d2093101105b3526a1036b51ad01553d30138f5beb8d2", size = 34543019, upload-time = "2026-02-10T12:04:15.065Z" }, - { url = "https://files.pythonhosted.org/packages/32/00/fbeb7d666ab8153f719e620bac5abfbc74640e8ec511612493110a75fe66/mysql_connector_python-9.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:3460ed976e1b88b7284335d9397a3c519dff56d71580ca1f76ff1c0c7714c813", size = 16515701, upload-time = "2026-02-10T12:04:19.26Z" }, - { url = "https://files.pythonhosted.org/packages/70/51/13cc90b2a703784cd9a0aa0a6fce07946cf6a2abe7c8fd0b585562e250fc/mysql_connector_python-9.6.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:e2cc13cd3dcdb845d636e52c4e7a9509b63da09bec6ce1b3696be53a79847e2d", size = 17585800, upload-time = "2026-02-10T12:04:21.6Z" }, - { url = "https://files.pythonhosted.org/packages/c8/6b/ce7ab998fbdd17f35a1b54624365d039045cbb2d42bbc7b03f50d7597c7b/mysql_connector_python-9.6.0-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:a08c2149d4b52a010c4353f18c84716d18114a4ecd00b466ea34138de2c640f2", size = 18452823, upload-time = "2026-02-10T12:04:23.995Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bf/8157ed61d17878c33511dcb97c68ecaaaf6220bea5a2944ea4eba73cc63a/mysql_connector_python-9.6.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:b00228b985edd208b20f45c5e684c54e08e31e01bc1d8c3c18a36641c3be5bf7", size = 34171594, upload-time = "2026-02-10T12:04:27.401Z" }, - { url = "https://files.pythonhosted.org/packages/f7/06/5efdd28819afdb9f1487a62842fda4277febe128a3cd6e9090dbe0a6524e/mysql_connector_python-9.6.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4617ef5216da7ca32dd46afda61a1552807762434127413bba46fbe4379f59d4", size = 34542851, upload-time = "2026-02-10T12:04:31.021Z" }, - { url = "https://files.pythonhosted.org/packages/40/6a/26e08a4a79f159cd8e5b64eb10bd056e7735b65d4464d98641f59eb9ca3a/mysql_connector_python-9.6.0-cp314-cp314-win_amd64.whl", hash = "sha256:bc782f64ca00b6b933d4c6a35568f1349d115cc4434c849b5b9edc015bee3e62", size = 17002947, upload-time = "2026-02-10T12:04:34.386Z" }, - { url = "https://files.pythonhosted.org/packages/15/dd/b3250826c29cee7816de4409a2fe5e469a68b9a89f6bfaa5eed74f05532c/mysql_connector_python-9.6.0-py2.py3-none-any.whl", hash = "sha256:44b0fb57207ebc6ae05b5b21b7968a9ed33b29187fe87b38951bad2a334d75d5", size = 480527, upload-time = "2026-02-10T12:04:36.176Z" }, -] - -[[package]] -name = "narwhals" -version = "2.19.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/1a/bd3317c0bdbcd9ffb710ddf5250b32898f8f2c240be99494fe137feb77a7/narwhals-2.19.0.tar.gz", hash = "sha256:14fd7040b5ff211d415a82e4827b9d04c354e213e72a6d0730205ffd72e3b7ff", size = 623698, upload-time = "2026-04-06T15:50:58.786Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/37/72/e61e3091e0e00fae9d3a8ef85ece9d2cd4b5966058e1f2901ce42679eebf/narwhals-2.19.0-py3-none-any.whl", hash = "sha256:1f8dfa4a33a6dbff878c3e9be4c3b455dfcaf2a9322f1357db00e4e92e95b84b", size = 446991, upload-time = "2026-04-06T15:50:57.046Z" }, -] - -[[package]] -name = "numpy" -version = "2.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", size = 16976799, upload-time = "2026-03-29T13:18:15.438Z" }, - { url = "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", size = 14971552, upload-time = "2026-03-29T13:18:18.606Z" }, - { url = "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", size = 5476566, upload-time = "2026-03-29T13:18:21.532Z" }, - { url = "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", size = 6806482, upload-time = "2026-03-29T13:18:23.634Z" }, - { url = "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", size = 15973376, upload-time = "2026-03-29T13:18:26.677Z" }, - { url = "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", size = 16925137, upload-time = "2026-03-29T13:18:30.14Z" }, - { url = "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", size = 17329414, upload-time = "2026-03-29T13:18:33.733Z" }, - { url = "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5", size = 18658397, upload-time = "2026-03-29T13:18:37.511Z" }, - { url = "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", size = 6239499, upload-time = "2026-03-29T13:18:40.372Z" }, - { url = "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", size = 12614257, upload-time = "2026-03-29T13:18:42.95Z" }, - { url = "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", size = 10486775, upload-time = "2026-03-29T13:18:45.835Z" }, - { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" }, - { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" }, - { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" }, - { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" }, - { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" }, - { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" }, - { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" }, - { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" }, - { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" }, - { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, - { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, - { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, - { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, - { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, - { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, - { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, - { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, - { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, - { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, - { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, - { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, - { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, - { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, - { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, - { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, - { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, - { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, - { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, - { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, - { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, - { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, - { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, - { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, - { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, - { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, - { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, - { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, - { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, - { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, - { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, - { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, - { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, - { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, - { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, - { url = "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", size = 16854491, upload-time = "2026-03-29T13:21:38.03Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", size = 14895830, upload-time = "2026-03-29T13:21:41.509Z" }, - { url = "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", size = 5400927, upload-time = "2026-03-29T13:21:44.747Z" }, - { url = "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", size = 6715557, upload-time = "2026-03-29T13:21:47.406Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", size = 15804253, upload-time = "2026-03-29T13:21:50.753Z" }, - { url = "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", size = 16753552, upload-time = "2026-03-29T13:21:54.344Z" }, - { url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" }, -] - -[[package]] -name = "onnxruntime" -version = "1.24.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flatbuffers" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "sympy" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/69/6c40720201012c6af9aa7d4ecdd620e521bd806dc6269d636fdd5c5aeebe/onnxruntime-1.24.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0bdfce8e9a6497cec584aab407b71bf697dac5e1b7b7974adc50bf7533bdb3a2", size = 17332131, upload-time = "2026-03-17T22:05:49.005Z" }, - { url = "https://files.pythonhosted.org/packages/38/e9/8c901c150ce0c368da38638f44152fb411059c0c7364b497c9e5c957321a/onnxruntime-1.24.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:046ff290045a387676941a02a8ae5c3ebec6b4f551ae228711968c4a69d8f6b7", size = 15152472, upload-time = "2026-03-17T22:03:26.176Z" }, - { url = "https://files.pythonhosted.org/packages/d5/b6/7a4df417cdd01e8f067a509e123ac8b31af450a719fa7ed81787dd6057ec/onnxruntime-1.24.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e54ad52e61d2d4618dcff8fa1480ac66b24ee2eab73331322db1049f11ccf330", size = 17222993, upload-time = "2026-03-17T22:04:34.485Z" }, - { url = "https://files.pythonhosted.org/packages/dd/59/8febe015f391aa1757fa5ba82c759ea4b6c14ef970132efb5e316665ba61/onnxruntime-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b43b63eb24a2bc8fc77a09be67587a570967a412cccb837b6245ccb546691153", size = 12594863, upload-time = "2026-03-17T22:05:38.749Z" }, - { url = "https://files.pythonhosted.org/packages/32/84/4155fcd362e8873eb6ce305acfeeadacd9e0e59415adac474bea3d9281bb/onnxruntime-1.24.4-cp311-cp311-win_arm64.whl", hash = "sha256:e26478356dba25631fb3f20112e345f8e8bf62c499bb497e8a559f7d69cf7e7b", size = 12259895, upload-time = "2026-03-17T22:05:28.812Z" }, - { url = "https://files.pythonhosted.org/packages/d7/38/31db1b232b4ba960065a90c1506ad7a56995cd8482033184e97fadca17cc/onnxruntime-1.24.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cad1c2b3f455c55678ab2a8caa51fb420c25e6e3cf10f4c23653cdabedc8de78", size = 17341875, upload-time = "2026-03-17T22:05:51.669Z" }, - { url = "https://files.pythonhosted.org/packages/aa/60/c4d1c8043eb42f8a9aa9e931c8c293d289c48ff463267130eca97d13357f/onnxruntime-1.24.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a5c5a544b22f90859c88617ecb30e161ee3349fcc73878854f43d77f00558b5", size = 15172485, upload-time = "2026-03-17T22:03:32.182Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ab/5b68110e0460d73fad814d5bd11c7b1ddcce5c37b10177eb264d6a36e331/onnxruntime-1.24.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d640eb9f3782689b55cfa715094474cd5662f2f137be6a6f847a594b6e9705c", size = 17244912, upload-time = "2026-03-17T22:04:37.251Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f4/6b89e297b93704345f0f3f8c62229bee323ef25682a3f9b4f89a39324950/onnxruntime-1.24.4-cp312-cp312-win_amd64.whl", hash = "sha256:535b29475ca42b593c45fbb2152fbf1cdf3f287315bf650e6a724a0a1d065cdb", size = 12596856, upload-time = "2026-03-17T22:05:41.224Z" }, - { url = "https://files.pythonhosted.org/packages/43/06/8b8ec6e9e6a474fcd5d772453f627ad4549dfe3ab8c0bf70af5afcde551b/onnxruntime-1.24.4-cp312-cp312-win_arm64.whl", hash = "sha256:e6214096e14b7b52e3bee1903dc12dc7ca09cb65e26664668a4620cc5e6f9a90", size = 12270275, upload-time = "2026-03-17T22:05:31.132Z" }, - { url = "https://files.pythonhosted.org/packages/e9/f0/8a21ec0a97e40abb7d8da1e8b20fb9e1af509cc6d191f6faa75f73622fb2/onnxruntime-1.24.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e99a48078baaefa2b50fe5836c319499f71f13f76ed32d0211f39109147a49e0", size = 17341922, upload-time = "2026-03-17T22:03:56.364Z" }, - { url = "https://files.pythonhosted.org/packages/8b/25/d7908de8e08cee9abfa15b8aa82349b79733ae5865162a3609c11598805d/onnxruntime-1.24.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4aaed1e5e1aaacf2343c838a30a7c3ade78f13eeb16817411f929d04040a13", size = 15172290, upload-time = "2026-03-17T22:03:37.124Z" }, - { url = "https://files.pythonhosted.org/packages/7f/72/105ec27a78c5aa0154a7c0cd8c41c19a97799c3b12fc30392928997e3be3/onnxruntime-1.24.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e30c972bc02e072911aabb6891453ec73795386c0af2b761b65444b8a4c4745f", size = 17244738, upload-time = "2026-03-17T22:04:40.625Z" }, - { url = "https://files.pythonhosted.org/packages/05/fb/a592736d968c2f58e12de4d52088dda8e0e724b26ad5c0487263adb45875/onnxruntime-1.24.4-cp313-cp313-win_amd64.whl", hash = "sha256:3b6ba8b0181a3aa88edab00eb01424ffc06f42e71095a91186c2249415fcff93", size = 12597435, upload-time = "2026-03-17T22:05:43.826Z" }, - { url = "https://files.pythonhosted.org/packages/ad/04/ae2479e9841b64bd2eb44f8a64756c62593f896514369a11243b1b86ca5c/onnxruntime-1.24.4-cp313-cp313-win_arm64.whl", hash = "sha256:71d6a5c1821d6e8586a024000ece458db8f2fc0ecd050435d45794827ce81e19", size = 12269852, upload-time = "2026-03-17T22:05:33.353Z" }, - { url = "https://files.pythonhosted.org/packages/b4/af/a479a536c4398ffaf49fbbe755f45d5b8726bdb4335ab31b537f3d7149b8/onnxruntime-1.24.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1700f559c8086d06b2a4d5de51e62cb4ff5e2631822f71a36db8c72383db71ee", size = 15176861, upload-time = "2026-03-17T22:03:40.143Z" }, - { url = "https://files.pythonhosted.org/packages/be/13/19f5da70c346a76037da2c2851ecbf1266e61d7f0dcdb887c667210d4608/onnxruntime-1.24.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c74e268dc808e61e63784d43f9ddcdaf50a776c2819e8bd1d1b11ef64bf7e36", size = 17247454, upload-time = "2026-03-17T22:04:46.643Z" }, - { url = "https://files.pythonhosted.org/packages/89/db/b30dbbd6037847b205ab75d962bc349bf1e46d02a65b30d7047a6893ffd6/onnxruntime-1.24.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:fbff2a248940e3398ae78374c5a839e49a2f39079b488bc64439fa0ec327a3e4", size = 17343300, upload-time = "2026-03-17T22:03:59.223Z" }, - { url = "https://files.pythonhosted.org/packages/61/88/1746c0e7959961475b84c776d35601a21d445f463c93b1433a409ec3e188/onnxruntime-1.24.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2b7969e72d8cb53ffc88ab6d49dd5e75c1c663bda7be7eb0ece192f127343d1", size = 15175936, upload-time = "2026-03-17T22:03:43.671Z" }, - { url = "https://files.pythonhosted.org/packages/5f/ba/4699cde04a52cece66cbebc85bd8335a0d3b9ad485abc9a2e15946a1349d/onnxruntime-1.24.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14ed1f197fab812b695a5eaddb536c635e58a2fbbe50a517c78f082cc6ce9177", size = 17246432, upload-time = "2026-03-17T22:04:49.58Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/4590910841bb28bd3b4b388a9efbedf4e2d2cca99ddf0c863642b4e87814/onnxruntime-1.24.4-cp314-cp314-win_amd64.whl", hash = "sha256:311e309f573bf3c12aa5723e23823077f83d5e412a18499d4485c7eb41040858", size = 12903276, upload-time = "2026-03-17T22:05:46.349Z" }, - { url = "https://files.pythonhosted.org/packages/7f/6f/60e2c0acea1e1ac09b3e794b5a19c166eebf91c0b860b3e6db8e74983fda/onnxruntime-1.24.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f0b910e86b759a4732663ec61fd57ac42ee1b0066f68299de164220b660546d", size = 12594365, upload-time = "2026-03-17T22:05:35.795Z" }, - { url = "https://files.pythonhosted.org/packages/cf/68/0c05d10f8f6c40fe0912ebec0d5a33884aaa2af2053507e864dab0883208/onnxruntime-1.24.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa12ddc54c9c4594073abcaa265cd9681e95fb89dae982a6f508a794ca42e661", size = 15176889, upload-time = "2026-03-17T22:03:48.021Z" }, - { url = "https://files.pythonhosted.org/packages/6c/1d/1666dc64e78d8587d168fec4e3b7922b92eb286a2ddeebcf6acb55c7dc82/onnxruntime-1.24.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1cc6a518255f012134bc791975a6294806be9a3b20c4a54cca25194c90cf731", size = 17247021, upload-time = "2026-03-17T22:04:52.377Z" }, -] - -[[package]] -name = "opensearch-dsl" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opensearch-py" }, - { name = "python-dateutil" }, - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e6/f9/ebf7132d42c367522c5cface5c92ec78218ed5778651d05d620627dd9371/opensearch-dsl-2.1.0.tar.gz", hash = "sha256:e54ad0d754358233503e0c08e85b77dbe07d6c00babeae62c81d8cee11965ae6", size = 67097, upload-time = "2023-03-21T23:43:56.272Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/28/1b06a9314f49892843f1559fb47c30d17771ba838ee01a3c0c638832101a/opensearch_dsl-2.1.0-py2.py3-none-any.whl", hash = "sha256:31559b738b48ed5abe87b357205a040fa1dc64042a6454ad2d6854050d911ba0", size = 63775, upload-time = "2023-03-21T23:43:54.287Z" }, -] - -[[package]] -name = "opensearch-py" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "events" }, - { name = "python-dateutil" }, - { name = "requests" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b8/58/ecec7f855aae7bcfb08f570088c6cb993f68c361a0727abab35dbf021acb/opensearch_py-3.0.0.tar.gz", hash = "sha256:ebb38f303f8a3f794db816196315bcddad880be0dc75094e3334bc271db2ed39", size = 248890, upload-time = "2025-06-17T05:39:48.453Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/e0/69fd114c607b0323d3f864ab4a5ecb87d76ec5a172d2e36a739c8baebea1/opensearch_py-3.0.0-py3-none-any.whl", hash = "sha256:842bf5d56a4a0d8290eda9bb921c50f3080e5dc4e5fefb9c9648289da3f6a8bb", size = 371491, upload-time = "2025-06-17T05:39:46.539Z" }, -] - -[[package]] -name = "orjson" -version = "3.11.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/1b/2024d06792d0779f9dbc51531b61c24f76c75b9f4ce05e6f3377a1814cea/orjson-3.11.8.tar.gz", hash = "sha256:96163d9cdc5a202703e9ad1b9ae757d5f0ca62f4fa0cc93d1f27b0e180cc404e", size = 5603832, upload-time = "2026-03-31T16:16:27.878Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/41/5aa7fa3b0f4dc6b47dcafc3cea909299c37e40e9972feabc8b6a74e2730d/orjson-3.11.8-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:003646067cc48b7fcab2ae0c562491c9b5d2cbd43f1e5f16d98fd118c5522d34", size = 229229, upload-time = "2026-03-31T16:14:50.424Z" }, - { url = "https://files.pythonhosted.org/packages/0a/d7/57e7f2458e0a2c41694f39fc830030a13053a84f837a5b73423dca1f0938/orjson-3.11.8-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ed193ce51d77a3830cad399a529cd4ef029968761f43ddc549e1bc62b40d88f8", size = 128871, upload-time = "2026-03-31T16:14:51.888Z" }, - { url = "https://files.pythonhosted.org/packages/53/4a/e0fdb9430983e6c46e0299559275025075568aad5d21dd606faee3703924/orjson-3.11.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30491bc4f862aa15744b9738517454f1e46e56c972a2be87d70d727d5b2a8f8", size = 132104, upload-time = "2026-03-31T16:14:53.142Z" }, - { url = "https://files.pythonhosted.org/packages/08/4a/2025a60ff3f5c8522060cda46612d9b1efa653de66ed2908591d8d82f22d/orjson-3.11.8-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eda5b8b6be91d3f26efb7dc6e5e68ee805bc5617f65a328587b35255f138bf4", size = 130483, upload-time = "2026-03-31T16:14:54.605Z" }, - { url = "https://files.pythonhosted.org/packages/2d/3c/b9cde05bdc7b2385c66014e0620627da638d3d04e4954416ab48c31196c5/orjson-3.11.8-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee8db7bfb6fe03581bbab54d7c4124a6dd6a7f4273a38f7267197890f094675f", size = 135481, upload-time = "2026-03-31T16:14:55.901Z" }, - { url = "https://files.pythonhosted.org/packages/ff/f2/a8238e7734de7cb589fed319857a8025d509c89dc52fdcc88f39c6d03d5a/orjson-3.11.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d8b5231de76c528a46b57010bbd83fb51e056aa0220a372fd5065e978406f1c", size = 146819, upload-time = "2026-03-31T16:14:57.548Z" }, - { url = "https://files.pythonhosted.org/packages/db/10/dbf1e2a3cafea673b1b4350e371877b759060d6018a998643b7040e5de48/orjson-3.11.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58a4a208a6fbfdb7a7327b8f201c6014f189f721fd55d047cafc4157af1bc62a", size = 132846, upload-time = "2026-03-31T16:14:58.91Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fc/55e667ec9c85694038fcff00573d221b085d50777368ee3d77f38668bf3c/orjson-3.11.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f8952d6d2505c003e8f0224ff7858d341fa4e33fef82b91c4ff0ef070f2393c", size = 133580, upload-time = "2026-03-31T16:15:00.519Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a6/c08c589a9aad0cb46c4831d17de212a2b6901f9d976814321ff8e69e8785/orjson-3.11.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0022bb50f90da04b009ce32c512dc1885910daa7cb10b7b0cba4505b16db82a8", size = 142042, upload-time = "2026-03-31T16:15:01.906Z" }, - { url = "https://files.pythonhosted.org/packages/5c/cc/2f78ea241d52b717d2efc38878615fe80425bf2beb6e68c984dde257a766/orjson-3.11.8-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ff51f9d657d1afb6f410cb435792ce4e1fe427aab23d2fcd727a2876e21d4cb6", size = 423845, upload-time = "2026-03-31T16:15:03.703Z" }, - { url = "https://files.pythonhosted.org/packages/70/07/c17dcf05dd8045457538428a983bf1f1127928df5bf328cb24d2b7cddacb/orjson-3.11.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6dbe9a97bdb4d8d9d5367b52a7c32549bba70b2739c58ef74a6964a6d05ae054", size = 147729, upload-time = "2026-03-31T16:15:05.203Z" }, - { url = "https://files.pythonhosted.org/packages/90/6c/0fb6e8a24e682e0958d71711ae6f39110e4b9cd8cab1357e2a89cb8e1951/orjson-3.11.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5c370674ebabe16c6ccac33ff80c62bf8a6e59439f5e9d40c1f5ab8fd2215b7", size = 136425, upload-time = "2026-03-31T16:15:07.052Z" }, - { url = "https://files.pythonhosted.org/packages/b2/35/4d3cc3a3d616035beb51b24a09bb872942dc452cf2df0c1d11ab35046d9f/orjson-3.11.8-cp311-cp311-win32.whl", hash = "sha256:0e32f7154299f42ae66f13488963269e5eccb8d588a65bc839ed986919fc9fac", size = 131870, upload-time = "2026-03-31T16:15:08.678Z" }, - { url = "https://files.pythonhosted.org/packages/13/26/9fe70f81d16b702f8c3a775e8731b50ad91d22dacd14c7599b60a0941cd1/orjson-3.11.8-cp311-cp311-win_amd64.whl", hash = "sha256:25e0c672a2e32348d2eb33057b41e754091f2835f87222e4675b796b92264f06", size = 127440, upload-time = "2026-03-31T16:15:09.994Z" }, - { url = "https://files.pythonhosted.org/packages/e8/c6/b038339f4145efd2859c1ca53097a52c0bb9cbdd24f947ebe146da1ad067/orjson-3.11.8-cp311-cp311-win_arm64.whl", hash = "sha256:9185589c1f2a944c17e26c9925dcdbc2df061cc4a145395c57f0c51f9b5dbfcd", size = 127399, upload-time = "2026-03-31T16:15:11.412Z" }, - { url = "https://files.pythonhosted.org/packages/01/f6/8d58b32ab32d9215973a1688aebd098252ee8af1766c0e4e36e7831f0295/orjson-3.11.8-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1cd0b77e77c95758f8e1100139844e99f3ccc87e71e6fc8e1c027e55807c549f", size = 229233, upload-time = "2026-03-31T16:15:12.762Z" }, - { url = "https://files.pythonhosted.org/packages/a9/8b/2ffe35e71f6b92622e8ea4607bf33ecf7dfb51b3619dcfabfd36cbe2d0a5/orjson-3.11.8-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:6a3d159d5ffa0e3961f353c4b036540996bf8b9697ccc38261c0eac1fd3347a6", size = 128772, upload-time = "2026-03-31T16:15:14.237Z" }, - { url = "https://files.pythonhosted.org/packages/27/d2/1f8682ae50d5c6897a563cb96bc106da8c9cb5b7b6e81a52e4cc086679b9/orjson-3.11.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76070a76e9c5ae661e2d9848f216980d8d533e0f8143e6ed462807b242e3c5e8", size = 131946, upload-time = "2026-03-31T16:15:15.607Z" }, - { url = "https://files.pythonhosted.org/packages/52/4b/5500f76f0eece84226e0689cb48dcde081104c2fa6e2483d17ca13685ffb/orjson-3.11.8-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54153d21520a71a4c82a0dbb4523e468941d549d221dc173de0f019678cf3813", size = 130368, upload-time = "2026-03-31T16:15:17.066Z" }, - { url = "https://files.pythonhosted.org/packages/da/4e/58b927e08fbe9840e6c920d9e299b051ea667463b1f39a56e668669f8508/orjson-3.11.8-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:469ac2125611b7c5741a0b3798cd9e5786cbad6345f9f400c77212be89563bec", size = 135540, upload-time = "2026-03-31T16:15:18.404Z" }, - { url = "https://files.pythonhosted.org/packages/56/7c/ba7cb871cba1bcd5cd02ee34f98d894c6cea96353ad87466e5aef2429c60/orjson-3.11.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14778ffd0f6896aa613951a7fbf4690229aa7a543cb2bfbe9f358e08aafa9546", size = 146877, upload-time = "2026-03-31T16:15:19.833Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/eb9c25fc1386696c6a342cd361c306452c75e0b55e86ad602dd4827a7fd7/orjson-3.11.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea56a955056a6d6c550cf18b3348656a9d9a4f02e2d0c02cabf3c73f1055d506", size = 132837, upload-time = "2026-03-31T16:15:21.282Z" }, - { url = "https://files.pythonhosted.org/packages/37/87/5ddeb7fc1fbd9004aeccab08426f34c81a5b4c25c7061281862b015fce2b/orjson-3.11.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53a0f57e59a530d18a142f4d4ba6dfc708dc5fdedce45e98ff06b44930a2a48f", size = 133624, upload-time = "2026-03-31T16:15:22.641Z" }, - { url = "https://files.pythonhosted.org/packages/22/09/90048793db94ee4b2fcec4ac8e5ddb077367637d6650be896b3494b79bb7/orjson-3.11.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b48e274f8824567d74e2158199e269597edf00823a1b12b63d48462bbf5123e", size = 141904, upload-time = "2026-03-31T16:15:24.435Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cf/eb284847487821a5d415e54149a6449ba9bfc5872ce63ab7be41b8ec401c/orjson-3.11.8-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3f262401086a3960586af06c054609365e98407151f5ea24a62893a40d80dbbb", size = 423742, upload-time = "2026-03-31T16:15:26.155Z" }, - { url = "https://files.pythonhosted.org/packages/44/09/e12423d327071c851c13e76936f144a96adacfc037394dec35ac3fc8d1e8/orjson-3.11.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e8c6218b614badf8e229b697865df4301afa74b791b6c9ade01d19a9953a942", size = 147806, upload-time = "2026-03-31T16:15:27.909Z" }, - { url = "https://files.pythonhosted.org/packages/b3/6d/37c2589ba864e582ffe7611643314785c6afb1f83c701654ef05daa8fcc7/orjson-3.11.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:093d489fa039ddade2db541097dbb484999fcc65fc2b0ff9819141e2ab364f25", size = 136485, upload-time = "2026-03-31T16:15:29.749Z" }, - { url = "https://files.pythonhosted.org/packages/be/c9/135194a02ab76b04ed9a10f68624b7ebd238bbe55548878b11ff15a0f352/orjson-3.11.8-cp312-cp312-win32.whl", hash = "sha256:e0950ed1bcb9893f4293fd5c5a7ee10934fbf82c4101c70be360db23ce24b7d2", size = 131966, upload-time = "2026-03-31T16:15:31.687Z" }, - { url = "https://files.pythonhosted.org/packages/ed/9a/9796f8fbe3cf30ce9cb696748dbb535e5c87be4bf4fe2e9ca498ef1fa8cf/orjson-3.11.8-cp312-cp312-win_amd64.whl", hash = "sha256:3cf17c141617b88ced4536b2135c552490f07799f6ad565948ea07bef0dcb9a6", size = 127441, upload-time = "2026-03-31T16:15:33.333Z" }, - { url = "https://files.pythonhosted.org/packages/cc/47/5aaf54524a7a4a0dd09dd778f3fa65dd2108290615b652e23d944152bc8e/orjson-3.11.8-cp312-cp312-win_arm64.whl", hash = "sha256:48854463b0572cc87dac7d981aa72ed8bf6deedc0511853dc76b8bbd5482d36d", size = 127364, upload-time = "2026-03-31T16:15:34.748Z" }, - { url = "https://files.pythonhosted.org/packages/66/7f/95fba509bb2305fab0073558f1e8c3a2ec4b2afe58ed9fcb7d3b8beafe94/orjson-3.11.8-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3f23426851d98478c8970da5991f84784a76682213cd50eb73a1da56b95239dc", size = 229180, upload-time = "2026-03-31T16:15:36.426Z" }, - { url = "https://files.pythonhosted.org/packages/f6/9d/b237215c743ca073697d759b5503abd2cb8a0d7b9c9e21f524bcf176ab66/orjson-3.11.8-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:ebaed4cef74a045b83e23537b52ef19a367c7e3f536751e355a2a394f8648559", size = 128754, upload-time = "2026-03-31T16:15:38.049Z" }, - { url = "https://files.pythonhosted.org/packages/42/3d/27d65b6d11e63f133781425f132807aef793ed25075fec686fc8e46dd528/orjson-3.11.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97c8f5d3b62380b70c36ffacb2a356b7c6becec86099b177f73851ba095ef623", size = 131877, upload-time = "2026-03-31T16:15:39.484Z" }, - { url = "https://files.pythonhosted.org/packages/dd/cc/faee30cd8f00421999e40ef0eba7332e3a625ce91a58200a2f52c7fef235/orjson-3.11.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:436c4922968a619fb7fef1ccd4b8b3a76c13b67d607073914d675026e911a65c", size = 130361, upload-time = "2026-03-31T16:15:41.274Z" }, - { url = "https://files.pythonhosted.org/packages/5c/bb/a6c55896197f97b6d4b4e7c7fd77e7235517c34f5d6ad5aadd43c54c6d7c/orjson-3.11.8-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ab359aff0436d80bfe8a23b46b5fea69f1e18aaf1760a709b4787f1318b317f", size = 135521, upload-time = "2026-03-31T16:15:42.758Z" }, - { url = "https://files.pythonhosted.org/packages/9c/7c/ca3a3525aa32ff636ebb1778e77e3587b016ab2edb1b618b36ba96f8f2c0/orjson-3.11.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f89b6d0b3a8d81e1929d3ab3d92bbc225688bd80a770c49432543928fe09ac55", size = 146862, upload-time = "2026-03-31T16:15:44.341Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0c/18a9d7f18b5edd37344d1fd5be17e94dc652c67826ab749c6e5948a78112/orjson-3.11.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c009e7a2ca9ad0ed1376ce20dd692146a5d9fe4310848904b6b4fee5c5c137", size = 132847, upload-time = "2026-03-31T16:15:46.368Z" }, - { url = "https://files.pythonhosted.org/packages/23/91/7e722f352ad67ca573cee44de2a58fb810d0f4eb4e33276c6a557979fd8a/orjson-3.11.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b895b781b3e395c067129d8551655642dfe9437273211d5404e87ac752b53", size = 133637, upload-time = "2026-03-31T16:15:48.123Z" }, - { url = "https://files.pythonhosted.org/packages/af/04/32845ce13ac5bd1046ddb02ac9432ba856cc35f6d74dde95864fe0ad5523/orjson-3.11.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:88006eda83858a9fdf73985ce3804e885c2befb2f506c9a3723cdeb5a2880e3e", size = 141906, upload-time = "2026-03-31T16:15:49.626Z" }, - { url = "https://files.pythonhosted.org/packages/02/5e/c551387ddf2d7106d9039369862245c85738b828844d13b99ccb8d61fd06/orjson-3.11.8-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:55120759e61309af7fcf9e961c6f6af3dde5921cdb3ee863ef63fd9db126cae6", size = 423722, upload-time = "2026-03-31T16:15:51.176Z" }, - { url = "https://files.pythonhosted.org/packages/00/a3/ecfe62434096f8a794d4976728cb59bcfc4a643977f21c2040545d37eb4c/orjson-3.11.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:98bdc6cb889d19bed01de46e67574a2eab61f5cc6b768ed50e8ac68e9d6ffab6", size = 147801, upload-time = "2026-03-31T16:15:52.939Z" }, - { url = "https://files.pythonhosted.org/packages/18/6d/0dce10b9f6643fdc59d99333871a38fa5a769d8e2fc34a18e5d2bfdee900/orjson-3.11.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:708c95f925a43ab9f34625e45dcdadf09ec8a6e7b664a938f2f8d5650f6c090b", size = 136460, upload-time = "2026-03-31T16:15:54.431Z" }, - { url = "https://files.pythonhosted.org/packages/01/d6/6dde4f31842d87099238f1f07b459d24edc1a774d20687187443ab044191/orjson-3.11.8-cp313-cp313-win32.whl", hash = "sha256:01c4e5a6695dc09098f2e6468a251bc4671c50922d4d745aff1a0a33a0cf5b8d", size = 131956, upload-time = "2026-03-31T16:15:56.081Z" }, - { url = "https://files.pythonhosted.org/packages/c1/f9/4e494a56e013db957fb77186b818b916d4695b8fa2aa612364974160e91b/orjson-3.11.8-cp313-cp313-win_amd64.whl", hash = "sha256:c154a35dd1330707450bb4d4e7dd1f17fa6f42267a40c1e8a1daa5e13719b4b8", size = 127410, upload-time = "2026-03-31T16:15:57.54Z" }, - { url = "https://files.pythonhosted.org/packages/57/7f/803203d00d6edb6e9e7eef421d4e1adbb5ea973e40b3533f3cfd9aeb374e/orjson-3.11.8-cp313-cp313-win_arm64.whl", hash = "sha256:4861bde57f4d253ab041e374f44023460e60e71efaa121f3c5f0ed457c3a701e", size = 127338, upload-time = "2026-03-31T16:15:59.106Z" }, - { url = "https://files.pythonhosted.org/packages/6d/35/b01910c3d6b85dc882442afe5060cbf719c7d1fc85749294beda23d17873/orjson-3.11.8-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ec795530a73c269a55130498842aaa762e4a939f6ce481a7e986eeaa790e9da4", size = 229171, upload-time = "2026-03-31T16:16:00.651Z" }, - { url = "https://files.pythonhosted.org/packages/c2/56/c9ec97bd11240abef39b9e5d99a15462809c45f677420fd148a6c5e6295e/orjson-3.11.8-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c492a0e011c0f9066e9ceaa896fbc5b068c54d365fea5f3444b697ee01bc8625", size = 128746, upload-time = "2026-03-31T16:16:02.673Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e4/66d4f30a90de45e2f0cbd9623588e8ae71eef7679dbe2ae954ed6d66a41f/orjson-3.11.8-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:883206d55b1bd5f5679ad5e6ddd3d1a5e3cac5190482927fdb8c78fb699193b5", size = 131867, upload-time = "2026-03-31T16:16:04.342Z" }, - { url = "https://files.pythonhosted.org/packages/19/30/2a645fc9286b928675e43fa2a3a16fb7b6764aa78cc719dc82141e00f30b/orjson-3.11.8-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5774c1fdcc98b2259800b683b19599c133baeb11d60033e2095fd9d4667b82db", size = 124664, upload-time = "2026-03-31T16:16:05.837Z" }, - { url = "https://files.pythonhosted.org/packages/db/44/77b9a86d84a28d52ba3316d77737f6514e17118119ade3f91b639e859029/orjson-3.11.8-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7381c83dd3d4a6347e6635950aa448f54e7b8406a27c7ecb4a37e9f1ae08b", size = 129701, upload-time = "2026-03-31T16:16:07.407Z" }, - { url = "https://files.pythonhosted.org/packages/b3/ea/eff3d9bfe47e9bc6969c9181c58d9f71237f923f9c86a2d2f490cd898c82/orjson-3.11.8-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14439063aebcb92401c11afc68ee4e407258d2752e62d748b6942dad20d2a70d", size = 141202, upload-time = "2026-03-31T16:16:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/52/c8/90d4b4c60c84d62068d0cf9e4d8f0a4e05e76971d133ac0c60d818d4db20/orjson-3.11.8-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa72e71977bff96567b0f500fc5bfd2fdf915f34052c782a4c6ebbdaa97aa858", size = 127194, upload-time = "2026-03-31T16:16:11.02Z" }, - { url = "https://files.pythonhosted.org/packages/8d/c7/ea9e08d1f0ba981adffb629811148b44774d935171e7b3d780ae43c4c254/orjson-3.11.8-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7679bc2f01bb0d219758f1a5f87bb7c8a81c0a186824a393b366876b4948e14f", size = 133639, upload-time = "2026-03-31T16:16:13.434Z" }, - { url = "https://files.pythonhosted.org/packages/6c/8c/ddbbfd6ba59453c8fc7fe1d0e5983895864e264c37481b2a791db635f046/orjson-3.11.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:14f7b8fcb35ef403b42fa5ecfa4ed032332a91f3dc7368fbce4184d59e1eae0d", size = 141914, upload-time = "2026-03-31T16:16:14.955Z" }, - { url = "https://files.pythonhosted.org/packages/4e/31/dbfbefec9df060d34ef4962cd0afcb6fa7a9ec65884cb78f04a7859526c3/orjson-3.11.8-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c2bdf7b2facc80b5e34f48a2d557727d5c5c57a8a450de122ae81fa26a81c1bc", size = 423800, upload-time = "2026-03-31T16:16:16.594Z" }, - { url = "https://files.pythonhosted.org/packages/87/cf/f74e9ae9803d4ab46b163494adba636c6d7ea955af5cc23b8aaa94cfd528/orjson-3.11.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ccd7ba1b0605813a0715171d39ec4c314cb97a9c85893c2c5c0c3a3729df38bf", size = 147837, upload-time = "2026-03-31T16:16:18.585Z" }, - { url = "https://files.pythonhosted.org/packages/64/e6/9214f017b5db85e84e68602792f742e5dc5249e963503d1b356bee611e01/orjson-3.11.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbc8c9c02463fef4d3c53a9ba3336d05496ec8e1f1c53326a1e4acc11f5c600", size = 136441, upload-time = "2026-03-31T16:16:20.151Z" }, - { url = "https://files.pythonhosted.org/packages/24/dd/3590348818f58f837a75fb969b04cdf187ae197e14d60b5e5a794a38b79d/orjson-3.11.8-cp314-cp314-win32.whl", hash = "sha256:0b57f67710a8cd459e4e54eb96d5f77f3624eba0c661ba19a525807e42eccade", size = 131983, upload-time = "2026-03-31T16:16:21.823Z" }, - { url = "https://files.pythonhosted.org/packages/3f/0f/b6cb692116e05d058f31ceee819c70f097fa9167c82f67fabe7516289abc/orjson-3.11.8-cp314-cp314-win_amd64.whl", hash = "sha256:735e2262363dcbe05c35e3a8869898022af78f89dde9e256924dc02e99fe69ca", size = 127396, upload-time = "2026-03-31T16:16:23.685Z" }, - { url = "https://files.pythonhosted.org/packages/c0/d1/facb5b5051fabb0ef9d26c6544d87ef19a939a9a001198655d0d891062dd/orjson-3.11.8-cp314-cp314-win_arm64.whl", hash = "sha256:6ccdea2c213cf9f3d9490cbd5d427693c870753df41e6cb375bd79bcbafc8817", size = 127330, upload-time = "2026-03-31T16:16:25.496Z" }, -] - -[[package]] -name = "oss2" -version = "2.19.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aliyun-python-sdk-core" }, - { name = "aliyun-python-sdk-kms" }, - { name = "crcmod" }, - { name = "pycryptodome" }, - { name = "requests" }, - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/b5/f2cb1950dda46ac2284d6c950489fdacd0e743c2d79a347924d3cc44b86f/oss2-2.19.1.tar.gz", hash = "sha256:a8ab9ee7eb99e88a7e1382edc6ea641d219d585a7e074e3776e9dec9473e59c1", size = 298845, upload-time = "2024-10-25T11:37:46.638Z" } - -[[package]] -name = "overrides" -version = "7.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, -] - -[[package]] -name = "packaging" -version = "24.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950, upload-time = "2024-11-08T09:47:47.202Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" }, -] - -[[package]] -name = "pandas" -version = "2.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, - { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, - { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, - { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, - { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, - { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, - { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, - { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, - { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, - { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, - { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, - { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, - { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, - { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, - { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, - { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, - { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, - { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, - { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, - { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, - { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, - { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, - { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, - { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, - { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, - { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, - { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, - { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, - { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, - { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, - { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, - { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, - { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, - { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, - { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, - { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, - { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, -] - -[[package]] -name = "pathspec" -version = "1.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, -] - -[[package]] -name = "pbr" -version = "7.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/ab/1de9a4f730edde1bdbbc2b8d19f8fa326f036b4f18b2f72cfbea7dc53c26/pbr-7.0.3.tar.gz", hash = "sha256:b46004ec30a5324672683ec848aed9e8fc500b0d261d40a3229c2d2bbfcedc29", size = 135625, upload-time = "2025-11-03T17:04:56.274Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/db/61efa0d08a99f897ef98256b03e563092d36cc38dc4ebe4a85020fe40b31/pbr-7.0.3-py2.py3-none-any.whl", hash = "sha256:ff223894eb1cd271a98076b13d3badff3bb36c424074d26334cd25aebeecea6b", size = 131898, upload-time = "2025-11-03T17:04:54.875Z" }, -] - -[[package]] -name = "pgvecto-rs" -version = "0.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "toml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/09/c0be8f54386367159fd22495635fba65ac6bbc436a34502bc2849d89f6ab/pgvecto_rs-0.2.2.tar.gz", hash = "sha256:edaa913d1747152b1407cbdf6337d51ac852547b54953ef38997433be3a75a3b", size = 28561, upload-time = "2024-10-08T02:01:15.678Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/dc/a39ceb4fe4b72f889228119b91e0ef7fcaaf9ec662ab19acdacb74cd5eaf/pgvecto_rs-0.2.2-py3-none-any.whl", hash = "sha256:5f3f7f806813de408c45dc10a9eb418b986c4d7b7723e8fce9298f2f7d8fbbd5", size = 30779, upload-time = "2024-10-08T02:01:14.669Z" }, -] - -[package.optional-dependencies] -psycopg3 = [ - { name = "psycopg", extra = ["binary"] }, -] - -[[package]] -name = "pgvector" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/25/6c/6d8b4b03b958c02fa8687ec6063c49d952a189f8c91ebbe51e877dfab8f7/pgvector-0.4.2.tar.gz", hash = "sha256:322cac0c1dc5d41c9ecf782bd9991b7966685dee3a00bc873631391ed949513a", size = 31354, upload-time = "2025-12-05T01:07:17.87Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/26/6cee8a1ce8c43625ec561aff19df07f9776b7525d9002c86bceb3e0ac970/pgvector-0.4.2-py3-none-any.whl", hash = "sha256:549d45f7a18593783d5eec609ea1684a724ba8405c4cb182a0b2b08aeff04e08", size = 27441, upload-time = "2025-12-05T01:07:16.536Z" }, -] - -[[package]] -name = "pillow" -version = "11.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/26/77f8ed17ca4ffd60e1dcd220a6ec6d71210ba398cfa33a13a1cd614c5613/pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722", size = 5316531, upload-time = "2025-07-01T09:13:59.203Z" }, - { url = "https://files.pythonhosted.org/packages/cb/39/ee475903197ce709322a17a866892efb560f57900d9af2e55f86db51b0a5/pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288", size = 4686560, upload-time = "2025-07-01T09:14:01.101Z" }, - { url = "https://files.pythonhosted.org/packages/d5/90/442068a160fd179938ba55ec8c97050a612426fae5ec0a764e345839f76d/pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d", size = 5870978, upload-time = "2025-07-03T13:09:55.638Z" }, - { url = "https://files.pythonhosted.org/packages/13/92/dcdd147ab02daf405387f0218dcf792dc6dd5b14d2573d40b4caeef01059/pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494", size = 7641168, upload-time = "2025-07-03T13:10:00.37Z" }, - { url = "https://files.pythonhosted.org/packages/6e/db/839d6ba7fd38b51af641aa904e2960e7a5644d60ec754c046b7d2aee00e5/pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58", size = 5973053, upload-time = "2025-07-01T09:14:04.491Z" }, - { url = "https://files.pythonhosted.org/packages/f2/2f/d7675ecae6c43e9f12aa8d58b6012683b20b6edfbdac7abcb4e6af7a3784/pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f", size = 6640273, upload-time = "2025-07-01T09:14:06.235Z" }, - { url = "https://files.pythonhosted.org/packages/45/ad/931694675ede172e15b2ff03c8144a0ddaea1d87adb72bb07655eaffb654/pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e", size = 6082043, upload-time = "2025-07-01T09:14:07.978Z" }, - { url = "https://files.pythonhosted.org/packages/3a/04/ba8f2b11fc80d2dd462d7abec16351b45ec99cbbaea4387648a44190351a/pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94", size = 6715516, upload-time = "2025-07-01T09:14:10.233Z" }, - { url = "https://files.pythonhosted.org/packages/48/59/8cd06d7f3944cc7d892e8533c56b0acb68399f640786313275faec1e3b6f/pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0", size = 6274768, upload-time = "2025-07-01T09:14:11.921Z" }, - { url = "https://files.pythonhosted.org/packages/f1/cc/29c0f5d64ab8eae20f3232da8f8571660aa0ab4b8f1331da5c2f5f9a938e/pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac", size = 6986055, upload-time = "2025-07-01T09:14:13.623Z" }, - { url = "https://files.pythonhosted.org/packages/c6/df/90bd886fabd544c25addd63e5ca6932c86f2b701d5da6c7839387a076b4a/pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd", size = 2423079, upload-time = "2025-07-01T09:14:15.268Z" }, - { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800, upload-time = "2025-07-01T09:14:17.648Z" }, - { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296, upload-time = "2025-07-01T09:14:19.828Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726, upload-time = "2025-07-03T13:10:04.448Z" }, - { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652, upload-time = "2025-07-03T13:10:10.391Z" }, - { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787, upload-time = "2025-07-01T09:14:21.63Z" }, - { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236, upload-time = "2025-07-01T09:14:23.321Z" }, - { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950, upload-time = "2025-07-01T09:14:25.237Z" }, - { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358, upload-time = "2025-07-01T09:14:27.053Z" }, - { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079, upload-time = "2025-07-01T09:14:30.104Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324, upload-time = "2025-07-01T09:14:31.899Z" }, - { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067, upload-time = "2025-07-01T09:14:33.709Z" }, - { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" }, - { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" }, - { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" }, - { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" }, - { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" }, - { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" }, - { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" }, - { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" }, - { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" }, - { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" }, - { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" }, - { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" }, - { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" }, - { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" }, - { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" }, - { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" }, - { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" }, - { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" }, - { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" }, - { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" }, - { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" }, - { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" }, - { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" }, - { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" }, - { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" }, - { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" }, - { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" }, - { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" }, - { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" }, - { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" }, - { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" }, - { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e3/6fa84033758276fb31da12e5fb66ad747ae83b93c67af17f8c6ff4cc8f34/pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6", size = 5270566, upload-time = "2025-07-01T09:16:19.801Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ee/e8d2e1ab4892970b561e1ba96cbd59c0d28cf66737fc44abb2aec3795a4e/pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438", size = 4654618, upload-time = "2025-07-01T09:16:21.818Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6d/17f80f4e1f0761f02160fc433abd4109fa1548dcfdca46cfdadaf9efa565/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3", size = 4874248, upload-time = "2025-07-03T13:11:20.738Z" }, - { url = "https://files.pythonhosted.org/packages/de/5f/c22340acd61cef960130585bbe2120e2fd8434c214802f07e8c03596b17e/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c", size = 6583963, upload-time = "2025-07-03T13:11:26.283Z" }, - { url = "https://files.pythonhosted.org/packages/31/5e/03966aedfbfcbb4d5f8aa042452d3361f325b963ebbadddac05b122e47dd/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361", size = 4957170, upload-time = "2025-07-01T09:16:23.762Z" }, - { url = "https://files.pythonhosted.org/packages/cc/2d/e082982aacc927fc2cab48e1e731bdb1643a1406acace8bed0900a61464e/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7", size = 5581505, upload-time = "2025-07-01T09:16:25.593Z" }, - { url = "https://files.pythonhosted.org/packages/34/e7/ae39f538fd6844e982063c3a5e4598b8ced43b9633baa3a85ef33af8c05c/pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8", size = 6984598, upload-time = "2025-07-01T09:16:27.732Z" }, -] - -[[package]] -name = "pinecone" -version = "8.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "orjson" }, - { name = "pinecone-plugin-assistant" }, - { name = "pinecone-plugin-interface" }, - { name = "python-dateutil" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0b/09/cb00dd61e61e7a840e0ea7c3bf017335a49bed596ac3df9bf1d1237dd833/pinecone-8.1.2.tar.gz", hash = "sha256:a86d433cd2fec85b118ec19a44ee740a687c6b8172f1aecace83137f00c0741f", size = 1043253, upload-time = "2026-04-08T18:06:33.442Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/09/19/09b66463203393e9de5e84f8f581aa1e84cb1bfe664ff10ecd22cb7b923d/pinecone-8.1.2-py3-none-any.whl", hash = "sha256:9f91477e9f765c8392805d582b4fad9fe9732ce816276e10d89921e7894c6653", size = 742810, upload-time = "2026-04-08T18:06:31.261Z" }, -] - -[[package]] -name = "pinecone-plugin-assistant" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/b5/308758742255b072e633947f9c016f5bf12aab1f592b4f81968e98fdf36d/pinecone_plugin_assistant-3.0.3.tar.gz", hash = "sha256:53f548ede60a95d79ff48d7865a3d0afd665ced9e77758272e62ba0c6c63bd26", size = 151895, upload-time = "2026-03-27T22:13:41.008Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/0d/188293ade97616983cc6764d64867322dc40a4ee7009f21a1cf99f10149c/pinecone_plugin_assistant-3.0.3-py3-none-any.whl", hash = "sha256:a42cff1e0b28df40596e440527c41888881eefedb1008117819506db60273808", size = 280675, upload-time = "2026-03-27T22:13:42.292Z" }, -] - -[[package]] -name = "pinecone-plugin-interface" -version = "0.0.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/fb/e8a4063264953ead9e2b24d9b390152c60f042c951c47f4592e9996e57ff/pinecone_plugin_interface-0.0.7.tar.gz", hash = "sha256:b8e6675e41847333aa13923cc44daa3f85676d7157324682dc1640588a982846", size = 3370, upload-time = "2024-06-05T01:57:52.093Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/1d/a21fdfcd6d022cb64cef5c2a29ee6691c6c103c4566b41646b080b7536a5/pinecone_plugin_interface-0.0.7-py3-none-any.whl", hash = "sha256:875857ad9c9fc8bbc074dbe780d187a2afd21f5bfe0f3b08601924a61ef1bba8", size = 6249, upload-time = "2024-06-05T01:57:50.583Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.9.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, -] - -[[package]] -name = "plotly" -version = "6.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "narwhals" }, - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/7f/0f100df1172aadf88a929a9dbb902656b0880ba4b960fe5224867159d8f4/plotly-6.7.0.tar.gz", hash = "sha256:45eea0ff27e2a23ccd62776f77eb43aa1ca03df4192b76036e380bb479b892c6", size = 6911286, upload-time = "2026-04-09T20:36:45.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl", hash = "sha256:ac8aca1c25c663a59b5b9140a549264a5badde2e057d79b8c772ae2920e32ff0", size = 9898444, upload-time = "2026-04-09T20:36:39.812Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "polars" -version = "1.39.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "polars-runtime-32" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/ab/f19e592fce9e000da49c96bf35e77cef67f9cb4b040bfa538a2764c0263e/polars-1.39.3.tar.gz", hash = "sha256:2e016c7f3e8d14fa777ef86fe0477cec6c67023a20ba4c94d6e8431eefe4a63c", size = 728987, upload-time = "2026-03-20T11:16:24.836Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/db/08f4ca10c5018813e7e0b59e4472302328b3d2ab1512f5a2157a814540e0/polars-1.39.3-py3-none-any.whl", hash = "sha256:c2b955ccc0a08a2bc9259785decf3d5c007b489b523bf2390cf21cec2bb82a56", size = 823985, upload-time = "2026-03-20T11:14:23.619Z" }, -] - -[[package]] -name = "polars-runtime-32" -version = "1.39.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/17/39/c8688696bc22b6c501e3b82ef3be10e543c07a785af5660f30997cd22dd2/polars_runtime_32-1.39.3.tar.gz", hash = "sha256:c728e4f469cafab501947585f36311b8fb222d3e934c6209e83791e0df20b29d", size = 2872335, upload-time = "2026-03-20T11:16:26.581Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/74/1b41205f7368c9375ab1dea91178eaa20435fe3eff036390a53a7660b416/polars_runtime_32-1.39.3-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:425c0b220b573fa097b4042edff73114cc6d23432a21dfd2dc41adf329d7d2e9", size = 45273243, upload-time = "2026-03-20T11:14:26.691Z" }, - { url = "https://files.pythonhosted.org/packages/90/bf/297716b3095fe719be20fcf7af1d2b6ab069c38199bbace2469608a69b3a/polars_runtime_32-1.39.3-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:ef5884711e3c617d7dc93519a7d038e242f5741cfe5fe9afd32d58845d86c562", size = 40842924, upload-time = "2026-03-20T11:14:31.154Z" }, - { url = "https://files.pythonhosted.org/packages/3d/3e/e65236d9d0d9babfa0ecba593413c06530fca60a8feb8f66243aa5dba92e/polars_runtime_32-1.39.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06b47f535eb1f97a9a1e5b0053ef50db3a4276e241178e37bbb1a38b1fa53b14", size = 43220650, upload-time = "2026-03-20T11:14:35.458Z" }, - { url = "https://files.pythonhosted.org/packages/b0/15/fc3e43f3fdf3f20b7dfb5abe871ab6162cf8fb4aeabf4cfad822d5dc4c79/polars_runtime_32-1.39.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bc9e13dc1d2e828331f2fe8ccbc9757554dc4933a8d3e85e906b988178f95ed", size = 46877498, upload-time = "2026-03-20T11:14:40.14Z" }, - { url = "https://files.pythonhosted.org/packages/3c/81/bd5f895919e32c6ab0a7786cd0c0ca961cb03152c47c3645808b54383f31/polars_runtime_32-1.39.3-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:363d49e3a3e638fc943e2b9887940300a7d06789930855a178a4727949259dc2", size = 43380176, upload-time = "2026-03-20T11:14:45.566Z" }, - { url = "https://files.pythonhosted.org/packages/7a/3e/c86433c3b5ec0315bdfc7640d0c15d41f1216c0103a0eab9a9b5147d6c4c/polars_runtime_32-1.39.3-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7c206bdcc7bc62ea038d6adea8e44b02f0e675e0191a54c810703b4895208ea4", size = 46485933, upload-time = "2026-03-20T11:14:51.155Z" }, - { url = "https://files.pythonhosted.org/packages/54/ce/200b310cf91f98e652eb6ea09fdb3a9718aa0293ebf113dce325797c8572/polars_runtime_32-1.39.3-cp310-abi3-win_amd64.whl", hash = "sha256:d66ca522517554a883446957539c40dc7b75eb0c2220357fb28bc8940d305339", size = 46995458, upload-time = "2026-03-20T11:14:56.074Z" }, - { url = "https://files.pythonhosted.org/packages/da/76/2d48927e0aa2abbdde08cbf4a2536883b73277d47fbeca95e952de86df34/polars_runtime_32-1.39.3-cp310-abi3-win_arm64.whl", hash = "sha256:f49f51461de63f13e5dd4eb080421c8f23f856945f3f8bd5b2b1f59da52c2860", size = 41857648, upload-time = "2026-03-20T11:15:01.142Z" }, -] - -[[package]] -name = "portalocker" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywin32", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/77/65b857a69ed876e1951e88aaba60f5ce6120c33703f7cb61a3c894b8c1b6/portalocker-3.2.0.tar.gz", hash = "sha256:1f3002956a54a8c3730586c5c77bf18fae4149e07eaf1c29fc3faf4d5a3f89ac", size = 95644, upload-time = "2025-06-14T13:20:40.03Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/a6/38c8e2f318bf67d338f4d629e93b0b4b9af331f455f0390ea8ce4a099b26/portalocker-3.2.0-py3-none-any.whl", hash = "sha256:3cdc5f565312224bc570c49337bd21428bba0ef363bbcf58b9ef4a9f11779968", size = 22424, upload-time = "2025-06-14T13:20:38.083Z" }, -] - -[[package]] -name = "posthog" -version = "7.10.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backoff" }, - { name = "distro" }, - { name = "python-dateutil" }, - { name = "requests" }, - { name = "six" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/10/48/dcb39417e903af8bc08e144a6adc594178ee42f276a858d570d3164c4262/posthog-7.10.3.tar.gz", hash = "sha256:dc624a07306dc2618dcfa9f9a706086db2efea33bb7ad0048625e5cf60e5401b", size = 187265, upload-time = "2026-04-08T17:43:10.931Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ec/d0050e82b4be2126f4769612f0ef9734348cb736a23754ea238202285343/posthog-7.10.3-py3-none-any.whl", hash = "sha256:bbaeb45dbfd56143343cf93bf091eb500f9996b95b797f4bf99b42905da54fe7", size = 217019, upload-time = "2026-04-08T17:43:09.305Z" }, -] - -[[package]] -name = "prometheus-client" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/fb/d9aa83ffe43ce1f19e557c0971d04b90561b0cfd50762aafb01968285553/prometheus_client-0.25.0.tar.gz", hash = "sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28", size = 86035, upload-time = "2026-04-09T19:53:42.359Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/9b/d4b1e644385499c8346fa9b622a3f030dce14cd6ef8a1871c221a17a67e7/prometheus_client-0.25.0-py3-none-any.whl", hash = "sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1", size = 64154, upload-time = "2026-04-09T19:53:41.324Z" }, -] - -[[package]] -name = "propcache" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, - { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, - { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, - { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, - { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, - { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, - { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, - { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, - { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, - { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, - { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, - { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, - { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, - { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, - { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, - { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, - { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, - { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, - { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, - { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, - { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, - { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, - { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, - { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, - { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, - { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, - { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, - { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, - { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, - { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, - { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, - { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, - { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, - { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, - { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, - { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, - { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, - { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, - { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, - { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, - { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, - { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, - { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, - { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, - { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, - { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, - { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, - { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, - { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, - { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, - { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, - { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, - { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, - { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, - { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, - { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, - { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, -] - -[[package]] -name = "protobuf" -version = "4.25.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d9/8e/d08c41a8c004e1d437ef467e7c4f9c3295cd784eba48ed5d1d01f94b1dad/protobuf-4.25.9.tar.gz", hash = "sha256:b0dc7e7c68de8b1ce831dacb12fb407e838edbb8b6cc0dc3a2a6b4cbf6de9cff", size = 381040, upload-time = "2026-03-25T23:09:36.423Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/e9/59435bd04bdd46cb38c42a336b22f9843e8e586ff83c35a5423f8b14704e/protobuf-4.25.9-cp310-abi3-win32.whl", hash = "sha256:bde396f568b0b46fc8fbfe9f02facf25b6755b2578a3b8ac61e74b9d69499e03", size = 392879, upload-time = "2026-03-25T23:09:21.32Z" }, - { url = "https://files.pythonhosted.org/packages/f3/16/42a5c7f1001783d2b5bfcecde10127f09010f78982c86ae409122ce3ece6/protobuf-4.25.9-cp310-abi3-win_amd64.whl", hash = "sha256:3683c05154252206f7cb2d371626514b3708199d9bcf683b503dabf3a2e38e06", size = 413900, upload-time = "2026-03-25T23:09:23.589Z" }, - { url = "https://files.pythonhosted.org/packages/56/5b/0074a0a9eb01f3d1c4648ca5e81b22090c811b210b61df9018ac6d6c5cda/protobuf-4.25.9-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:9560813560e6ee72c11ca8873878bdb7ee003c96a57ebb013245fe84e2540904", size = 394826, upload-time = "2026-03-25T23:09:25.194Z" }, - { url = "https://files.pythonhosted.org/packages/54/aa/b2dba856f64c36b2a06c67be1472de98cca07a2322d0f0cbf03279a40e5b/protobuf-4.25.9-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:999146ef02e7fa6a692477badd1528bcd7268df211852a3df2d834ba2b480791", size = 294191, upload-time = "2026-03-25T23:09:26.613Z" }, - { url = "https://files.pythonhosted.org/packages/a8/5c/53f18822017b8bda6bd8bb4e02048e911fdc79a3dafdc83ab994fe922a84/protobuf-4.25.9-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:438c636de8fb706a0de94a12a268ef1ae8f5ba5ae655a7671fcda5968ba3c9be", size = 295178, upload-time = "2026-03-25T23:09:27.839Z" }, - { url = "https://files.pythonhosted.org/packages/16/28/d5065b212685875d3924bcdb3201cbf467cb4d58a18aa19a8dfd99ea80a9/protobuf-4.25.9-py3-none-any.whl", hash = "sha256:d49b615e7c935194ac161f0965699ac84df6112c378e05ec53da65d2e4cbb6d4", size = 156822, upload-time = "2026-03-25T23:09:34.957Z" }, -] - -[[package]] -name = "psutil" -version = "7.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, - { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, - { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, - { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, - { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, - { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, - { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, - { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, - { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, - { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, - { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, - { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, - { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, - { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, - { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, - { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, - { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, - { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, -] - -[[package]] -name = "psycopg" -version = "3.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, - { name = "tzdata", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d3/b6/379d0a960f8f435ec78720462fd94c4863e7a31237cf81bf76d0af5883bf/psycopg-3.3.3.tar.gz", hash = "sha256:5e9a47458b3c1583326513b2556a2a9473a1001a56c9efe9e587245b43148dd9", size = 165624, upload-time = "2026-02-18T16:52:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/5b/181e2e3becb7672b502f0ed7f16ed7352aca7c109cfb94cf3878a9186db9/psycopg-3.3.3-py3-none-any.whl", hash = "sha256:f96525a72bcfade6584ab17e89de415ff360748c766f0106959144dcbb38c698", size = 212768, upload-time = "2026-02-18T16:46:27.365Z" }, -] - -[package.optional-dependencies] -binary = [ - { name = "psycopg-binary", marker = "implementation_name != 'pypy'" }, -] -pool = [ - { name = "psycopg-pool" }, -] - -[[package]] -name = "psycopg-binary" -version = "3.3.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/c0/b389119dd754483d316805260f3e73cdcad97925839107cc7a296f6132b1/psycopg_binary-3.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a89bb9ee11177b2995d87186b1d9fa892d8ea725e85eab28c6525e4cc14ee048", size = 4609740, upload-time = "2026-02-18T16:47:51.093Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e3/9976eef20f61840285174d360da4c820a311ab39d6b82fa09fbb545be825/psycopg_binary-3.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9f7d0cf072c6fbac3795b08c98ef9ea013f11db609659dcfc6b1f6cc31f9e181", size = 4676837, upload-time = "2026-02-18T16:47:55.523Z" }, - { url = "https://files.pythonhosted.org/packages/9f/f2/d28ba2f7404fd7f68d41e8a11df86313bd646258244cb12a8dd83b868a97/psycopg_binary-3.3.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:90eecd93073922f085967f3ed3a98ba8c325cbbc8c1a204e300282abd2369e13", size = 5497070, upload-time = "2026-02-18T16:47:59.929Z" }, - { url = "https://files.pythonhosted.org/packages/de/2f/6c5c54b815edeb30a281cfcea96dc93b3bb6be939aea022f00cab7aa1420/psycopg_binary-3.3.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dac7ee2f88b4d7bb12837989ca354c38d400eeb21bce3b73dac02622f0a3c8d6", size = 5172410, upload-time = "2026-02-18T16:48:05.665Z" }, - { url = "https://files.pythonhosted.org/packages/51/75/8206c7008b57de03c1ada46bd3110cc3743f3fd9ed52031c4601401d766d/psycopg_binary-3.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b62cf8784eb6d35beaee1056d54caf94ec6ecf2b7552395e305518ab61eb8fd2", size = 6763408, upload-time = "2026-02-18T16:48:13.541Z" }, - { url = "https://files.pythonhosted.org/packages/d4/5a/ea1641a1e6c8c8b3454b0fcb43c3045133a8b703e6e824fae134088e63bd/psycopg_binary-3.3.3-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a39f34c9b18e8f6794cca17bfbcd64572ca2482318db644268049f8c738f35a6", size = 5006255, upload-time = "2026-02-18T16:48:22.176Z" }, - { url = "https://files.pythonhosted.org/packages/aa/fb/538df099bf55ae1637d52d7ccb6b9620b535a40f4c733897ac2b7bb9e14c/psycopg_binary-3.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:883d68d48ca9ff3cb3d10c5fdebea02c79b48eecacdddbf7cce6e7cdbdc216b8", size = 4532694, upload-time = "2026-02-18T16:48:27.338Z" }, - { url = "https://files.pythonhosted.org/packages/a1/d1/00780c0e187ea3c13dfc53bd7060654b2232cd30df562aac91a5f1c545ac/psycopg_binary-3.3.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:cab7bc3d288d37a80aa8c0820033250c95e40b1c2b5c57cf59827b19c2a8b69d", size = 4222833, upload-time = "2026-02-18T16:48:31.221Z" }, - { url = "https://files.pythonhosted.org/packages/7a/34/a07f1ff713c51d64dc9f19f2c32be80299a2055d5d109d5853662b922cb4/psycopg_binary-3.3.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:56c767007ca959ca32f796b42379fc7e1ae2ed085d29f20b05b3fc394f3715cc", size = 3952818, upload-time = "2026-02-18T16:48:35.869Z" }, - { url = "https://files.pythonhosted.org/packages/d3/67/d33f268a7759b4445f3c9b5a181039b01af8c8263c865c1be7a6444d4749/psycopg_binary-3.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:da2f331a01af232259a21573a01338530c6016dcfad74626c01330535bcd8628", size = 4258061, upload-time = "2026-02-18T16:48:41.365Z" }, - { url = "https://files.pythonhosted.org/packages/b4/3b/0d8d2c5e8e29ccc07d28c8af38445d9d9abcd238d590186cac82ee71fc84/psycopg_binary-3.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:19f93235ece6dbfc4036b5e4f6d8b13f0b8f2b3eeb8b0bd2936d406991bcdd40", size = 3558915, upload-time = "2026-02-18T16:48:46.679Z" }, - { url = "https://files.pythonhosted.org/packages/90/15/021be5c0cbc5b7c1ab46e91cc3434eb42569f79a0592e67b8d25e66d844d/psycopg_binary-3.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6698dbab5bcef8fdb570fc9d35fd9ac52041771bfcfe6fd0fc5f5c4e36f1e99d", size = 4591170, upload-time = "2026-02-18T16:48:55.594Z" }, - { url = "https://files.pythonhosted.org/packages/f1/54/a60211c346c9a2f8c6b272b5f2bbe21f6e11800ce7f61e99ba75cf8b63e1/psycopg_binary-3.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:329ff393441e75f10b673ae99ab45276887993d49e65f141da20d915c05aafd8", size = 4670009, upload-time = "2026-02-18T16:49:03.608Z" }, - { url = "https://files.pythonhosted.org/packages/c1/53/ac7c18671347c553362aadbf65f92786eef9540676ca24114cc02f5be405/psycopg_binary-3.3.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:eb072949b8ebf4082ae24289a2b0fd724da9adc8f22743409d6fd718ddb379df", size = 5469735, upload-time = "2026-02-18T16:49:10.128Z" }, - { url = "https://files.pythonhosted.org/packages/7f/c3/4f4e040902b82a344eff1c736cde2f2720f127fe939c7e7565706f96dd44/psycopg_binary-3.3.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:263a24f39f26e19ed7fc982d7859a36f17841b05bebad3eb47bb9cd2dd785351", size = 5152919, upload-time = "2026-02-18T16:49:16.335Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e7/d929679c6a5c212bcf738806c7c89f5b3d0919f2e1685a0e08d6ff877945/psycopg_binary-3.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5152d50798c2fa5bd9b68ec68eb68a1b71b95126c1d70adaa1a08cd5eefdc23d", size = 6738785, upload-time = "2026-02-18T16:49:22.687Z" }, - { url = "https://files.pythonhosted.org/packages/69/b0/09703aeb69a9443d232d7b5318d58742e8ca51ff79f90ffe6b88f1db45e7/psycopg_binary-3.3.3-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9d6a1e56dd267848edb824dbeb08cf5bac649e02ee0b03ba883ba3f4f0bd54f2", size = 4979008, upload-time = "2026-02-18T16:49:27.313Z" }, - { url = "https://files.pythonhosted.org/packages/cc/a6/e662558b793c6e13a7473b970fee327d635270e41eded3090ef14045a6a5/psycopg_binary-3.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73eaaf4bb04709f545606c1db2f65f4000e8a04cdbf3e00d165a23004692093e", size = 4508255, upload-time = "2026-02-18T16:49:31.575Z" }, - { url = "https://files.pythonhosted.org/packages/5f/7f/0f8b2e1d5e0093921b6f324a948a5c740c1447fbb45e97acaf50241d0f39/psycopg_binary-3.3.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:162e5675efb4704192411eaf8e00d07f7960b679cd3306e7efb120bb8d9456cc", size = 4189166, upload-time = "2026-02-18T16:49:35.801Z" }, - { url = "https://files.pythonhosted.org/packages/92/ec/ce2e91c33bc8d10b00c87e2f6b0fb570641a6a60042d6a9ae35658a3a797/psycopg_binary-3.3.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:fab6b5e37715885c69f5d091f6ff229be71e235f272ebaa35158d5a46fd548a0", size = 3924544, upload-time = "2026-02-18T16:49:41.129Z" }, - { url = "https://files.pythonhosted.org/packages/c5/2f/7718141485f73a924205af60041c392938852aa447a94c8cbd222ff389a1/psycopg_binary-3.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a4aab31bd6d1057f287c96c0effca3a25584eb9cc702f282ecb96ded7814e830", size = 4235297, upload-time = "2026-02-18T16:49:46.726Z" }, - { url = "https://files.pythonhosted.org/packages/57/f9/1add717e2643a003bbde31b1b220172e64fbc0cb09f06429820c9173f7fc/psycopg_binary-3.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:59aa31fe11a0e1d1bcc2ce37ed35fe2ac84cd65bb9036d049b1a1c39064d0f14", size = 3547659, upload-time = "2026-02-18T16:49:52.999Z" }, - { url = "https://files.pythonhosted.org/packages/03/0a/cac9fdf1df16a269ba0e5f0f06cac61f826c94cadb39df028cdfe19d3a33/psycopg_binary-3.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05f32239aec25c5fb15f7948cffdc2dc0dac098e48b80a140e4ba32b572a2e7d", size = 4590414, upload-time = "2026-02-18T16:50:01.441Z" }, - { url = "https://files.pythonhosted.org/packages/9c/c0/d8f8508fbf440edbc0099b1abff33003cd80c9e66eb3a1e78834e3fb4fb9/psycopg_binary-3.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c84f9d214f2d1de2fafebc17fa68ac3f6561a59e291553dfc45ad299f4898c1", size = 4669021, upload-time = "2026-02-18T16:50:08.803Z" }, - { url = "https://files.pythonhosted.org/packages/04/05/097016b77e343b4568feddf12c72171fc513acef9a4214d21b9478569068/psycopg_binary-3.3.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e77957d2ba17cada11be09a5066d93026cdb61ada7c8893101d7fe1c6e1f3925", size = 5467453, upload-time = "2026-02-18T16:50:14.985Z" }, - { url = "https://files.pythonhosted.org/packages/91/23/73244e5feb55b5ca109cede6e97f32ef45189f0fdac4c80d75c99862729d/psycopg_binary-3.3.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:42961609ac07c232a427da7c87a468d3c82fee6762c220f38e37cfdacb2b178d", size = 5151135, upload-time = "2026-02-18T16:50:24.82Z" }, - { url = "https://files.pythonhosted.org/packages/11/49/5309473b9803b207682095201d8708bbc7842ddf3f192488a69204e36455/psycopg_binary-3.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae07a3114313dd91fce686cab2f4c44af094398519af0e0f854bc707e1aeedf1", size = 6737315, upload-time = "2026-02-18T16:50:35.106Z" }, - { url = "https://files.pythonhosted.org/packages/d4/5d/03abe74ef34d460b33c4d9662bf6ec1dd38888324323c1a1752133c10377/psycopg_binary-3.3.3-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d257c58d7b36a621dcce1d01476ad8b60f12d80eb1406aee4cf796f88b2ae482", size = 4979783, upload-time = "2026-02-18T16:50:42.067Z" }, - { url = "https://files.pythonhosted.org/packages/f0/6c/3fbf8e604e15f2f3752900434046c00c90bb8764305a1b81112bff30ba24/psycopg_binary-3.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:07c7211f9327d522c9c47560cae00a4ecf6687f4e02d779d035dd3177b41cb12", size = 4509023, upload-time = "2026-02-18T16:50:50.116Z" }, - { url = "https://files.pythonhosted.org/packages/9c/6b/1a06b43b7c7af756c80b67eac8bfaa51d77e68635a8a8d246e4f0bb7604a/psycopg_binary-3.3.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8e7e9eca9b363dbedeceeadd8be97149d2499081f3c52d141d7cd1f395a91f83", size = 4185874, upload-time = "2026-02-18T16:50:55.97Z" }, - { url = "https://files.pythonhosted.org/packages/2b/d3/bf49e3dcaadba510170c8d111e5e69e5ae3f981c1554c5bb71c75ce354bb/psycopg_binary-3.3.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:cb85b1d5702877c16f28d7b92ba030c1f49ebcc9b87d03d8c10bf45a2f1c7508", size = 3925668, upload-time = "2026-02-18T16:51:03.299Z" }, - { url = "https://files.pythonhosted.org/packages/f8/92/0aac830ed6a944fe334404e1687a074e4215630725753f0e3e9a9a595b62/psycopg_binary-3.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d4606c84d04b80f9138d72f1e28c6c02dc5ae0c7b8f3f8aaf89c681ce1cd1b1", size = 4234973, upload-time = "2026-02-18T16:51:09.097Z" }, - { url = "https://files.pythonhosted.org/packages/2e/96/102244653ee5a143ece5afe33f00f52fe64e389dfce8dbc87580c6d70d3d/psycopg_binary-3.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:74eae563166ebf74e8d950ff359be037b85723d99ca83f57d9b244a871d6c13b", size = 3551342, upload-time = "2026-02-18T16:51:13.892Z" }, - { url = "https://files.pythonhosted.org/packages/a2/71/7a57e5b12275fe7e7d84d54113f0226080423a869118419c9106c083a21c/psycopg_binary-3.3.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:497852c5eaf1f0c2d88ab74a64a8097c099deac0c71de1cbcf18659a8a04a4b2", size = 4607368, upload-time = "2026-02-18T16:51:19.295Z" }, - { url = "https://files.pythonhosted.org/packages/c7/04/cb834f120f2b2c10d4003515ef9ca9d688115b9431735e3936ae48549af8/psycopg_binary-3.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:258d1ea53464d29768bf25930f43291949f4c7becc706f6e220c515a63a24edd", size = 4687047, upload-time = "2026-02-18T16:51:23.84Z" }, - { url = "https://files.pythonhosted.org/packages/40/e9/47a69692d3da9704468041aa5ed3ad6fc7f6bb1a5ae788d261a26bbca6c7/psycopg_binary-3.3.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:111c59897a452196116db12e7f608da472fbff000693a21040e35fc978b23430", size = 5487096, upload-time = "2026-02-18T16:51:29.645Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b6/0e0dd6a2f802864a4ae3dbadf4ec620f05e3904c7842b326aafc43e5f464/psycopg_binary-3.3.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:17bb6600e2455993946385249a3c3d0af52cd70c1c1cdbf712e9d696d0b0bf1b", size = 5168720, upload-time = "2026-02-18T16:51:36.499Z" }, - { url = "https://files.pythonhosted.org/packages/6f/0d/977af38ac19a6b55d22dff508bd743fd7c1901e1b73657e7937c7cccb0a3/psycopg_binary-3.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:642050398583d61c9856210568eb09a8e4f2fe8224bf3be21b67a370e677eead", size = 6762076, upload-time = "2026-02-18T16:51:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/34/40/912a39d48322cf86895c0eaf2d5b95cb899402443faefd4b09abbba6b6e1/psycopg_binary-3.3.3-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:533efe6dc3a7cba5e2a84e38970786bb966306863e45f3db152007e9f48638a6", size = 4997623, upload-time = "2026-02-18T16:51:47.707Z" }, - { url = "https://files.pythonhosted.org/packages/98/0c/c14d0e259c65dc7be854d926993f151077887391d5a081118907a9d89603/psycopg_binary-3.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5958dbf28b77ce2033482f6cb9ef04d43f5d8f4b7636e6963d5626f000efb23e", size = 4532096, upload-time = "2026-02-18T16:51:51.421Z" }, - { url = "https://files.pythonhosted.org/packages/39/21/8b7c50a194cfca6ea0fd4d1f276158307785775426e90700ab2eba5cd623/psycopg_binary-3.3.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a6af77b6626ce92b5817bf294b4d45ec1a6161dba80fc2d82cdffdd6814fd023", size = 4208884, upload-time = "2026-02-18T16:51:57.336Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2c/a4981bf42cf30ebba0424971d7ce70a222ae9b82594c42fc3f2105d7b525/psycopg_binary-3.3.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:47f06fcbe8542b4d96d7392c476a74ada521c5aebdb41c3c0155f6595fc14c8d", size = 3944542, upload-time = "2026-02-18T16:52:04.266Z" }, - { url = "https://files.pythonhosted.org/packages/60/e9/b7c29b56aa0b85a4e0c4d89db691c1ceef08f46a356369144430c155a2f5/psycopg_binary-3.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7800e6c6b5dc4b0ca7cc7370f770f53ac83886b76afda0848065a674231e856", size = 4254339, upload-time = "2026-02-18T16:52:10.444Z" }, - { url = "https://files.pythonhosted.org/packages/98/5a/291d89f44d3820fffb7a04ebc8f3ef5dda4f542f44a5daea0c55a84abf45/psycopg_binary-3.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:165f22ab5a9513a3d7425ffb7fcc7955ed8ccaeef6d37e369d6cc1dff1582383", size = 3652796, upload-time = "2026-02-18T16:52:14.02Z" }, -] - -[[package]] -name = "psycopg-pool" -version = "3.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/56/9a/9470d013d0d50af0da9c4251614aeb3c1823635cab3edc211e3839db0bcf/psycopg_pool-3.3.0.tar.gz", hash = "sha256:fa115eb2860bd88fce1717d75611f41490dec6135efb619611142b24da3f6db5", size = 31606, upload-time = "2025-12-01T11:34:33.11Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/c3/26b8a0908a9db249de3b4169692e1c7c19048a9bc41a4d3209cee7dbb758/psycopg_pool-3.3.0-py3-none-any.whl", hash = "sha256:2e44329155c410b5e8666372db44276a8b1ebd8c90f1c3026ebba40d4bc81063", size = 39995, upload-time = "2025-12-01T11:34:29.761Z" }, -] - -[[package]] -name = "pulsar-client" -version = "3.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/02/667afd3608270646ded6ea7c87a9b1f857d73e31daa7aa11784169cc0d1d/pulsar_client-3.10.0-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:8c0db5ba524f2260bb9e289e0a568619d691cb4a017133c9058e1ecb8f7b93ef", size = 9604733, upload-time = "2026-02-05T15:17:34.991Z" }, - { url = "https://files.pythonhosted.org/packages/41/73/5f288474e30dc2932de7e408e382f2770dfa908bc4e423cb0913c77a9cd5/pulsar_client-3.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dfad27ddd9620eb3588e7dd8b5228937c5a62de13588c55d720804590f711e12", size = 6812805, upload-time = "2026-02-05T15:17:39.174Z" }, - { url = "https://files.pythonhosted.org/packages/8c/f3/e8a33df3d3247857331ca0a05b159a126c4aee7b6ea72451f3f0eb390fb8/pulsar_client-3.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:bf1aba146baa9141da7769cfcfc746d5bb73bf0520cac07b7ec3d5d4ffcf2e7c", size = 6747897, upload-time = "2026-02-05T15:17:45.814Z" }, - { url = "https://files.pythonhosted.org/packages/4f/67/963890a62bc5773aadd98d3878b439792d50324260b0b7bd3ed8e930a25c/pulsar_client-3.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:789cffb228719bc511561dafd3bb2eb16a806d9a02021df6c31927a9e381db6c", size = 7381581, upload-time = "2026-02-05T15:17:49.061Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a3/5fd06e40bee21c23e7b6e391feb56e924028e8e105423320d9dd9dba021a/pulsar_client-3.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c8eb58e41d67dac65f3299aacc098c0d79ca11c862e8409d163c983ca74ea5e8", size = 7209287, upload-time = "2026-02-05T15:17:52.684Z" }, - { url = "https://files.pythonhosted.org/packages/4a/0f/bf9d3d526deec42c731a80b471a9aa2d8d5842cfa4d3b65a94931f43f732/pulsar_client-3.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbec5f2a14a3fe003fafb7182157a8af1adfc3946ccd3d804604ffb6d8a892dd", size = 3701771, upload-time = "2026-02-05T15:17:56.629Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ad/4fe92d039bebfda31d26b2124145aac0470e0bfb79ce04978d2a6523af60/pulsar_client-3.10.0-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:bc18a06416a335bec8b6131ed751b4e849e055db581e0ec935f0d8e8adca8f3f", size = 9630238, upload-time = "2026-02-05T15:18:04.106Z" }, - { url = "https://files.pythonhosted.org/packages/da/95/123a7be7ac7e5df9c23af5cbeef1d51151e9807aaf892bfa42c124f03b8f/pulsar_client-3.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:7b32afa1ecb3ffea570b0c4d5766140b7c266c6e43e247b64b0a10ed59230807", size = 6819435, upload-time = "2026-02-05T15:18:07.784Z" }, - { url = "https://files.pythonhosted.org/packages/9a/84/d2c620eb0ea77ce6535c8b12690775971f81291ae893e36a2eb6e600e2dc/pulsar_client-3.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e47d6d10f2e2408cf98e5493f62b6259f6b3bc6b75b56ee57f034aa92b58f7d5", size = 6757864, upload-time = "2026-02-05T15:18:11.803Z" }, - { url = "https://files.pythonhosted.org/packages/b5/19/cdec899249d0b1e08d689f33cbd3752e0022fbd40e44921be5acc388c9ec/pulsar_client-3.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:92a5f92fa4baebd9f7af1d8c3ed5028430074972e115b20a2db85693aec867c8", size = 7384283, upload-time = "2026-02-05T15:18:15.27Z" }, - { url = "https://files.pythonhosted.org/packages/e6/2c/0632e85751f81f59079c16a60e2be932f0432da7dfc5c6dcccd90a15e4e2/pulsar_client-3.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16e183cfe5696888a0ede4ebe8741a42eb443200e3688ab1b1e3355dec2364f0", size = 7218433, upload-time = "2026-02-05T15:18:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/4d/10/25884bd4d211b97c5e3f087b7ca763b4aec70fced3c8beb0b511ad523690/pulsar_client-3.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:ed14052be5d6d79a73ca64c61eb5c31a313e04f02718bd661d673d03c908853e", size = 3702227, upload-time = "2026-02-05T15:18:20.954Z" }, - { url = "https://files.pythonhosted.org/packages/e5/ca/e21bab5d21e2a5b292e7589bfdae6d63277e037893e568a97ea3a4914acf/pulsar_client-3.10.0-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:6fd7c2cebf3090afcc8e00cee7608dd7667be82de5467980cdb02c516a747434", size = 9630379, upload-time = "2026-02-05T15:18:28.247Z" }, - { url = "https://files.pythonhosted.org/packages/d8/87/08e41273f8ddbe137336b904e95249cd4b9e2b1cbf175987791d2d541009/pulsar_client-3.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:65998e8cbe3b793f7664bb939caf857e53799ec40cfa38b25686dd55dea4af42", size = 6822140, upload-time = "2026-02-05T15:18:31.644Z" }, - { url = "https://files.pythonhosted.org/packages/8c/e8/659e5abc70c45aa3ab1cd9cddf31c9999bc898aae4cf0d3757a59faf8d56/pulsar_client-3.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:a1d5ccd9b80371b60fb2cdbd27979171ffa77f5666c70c421746abbc39e7afa5", size = 6758711, upload-time = "2026-02-05T15:18:35.292Z" }, - { url = "https://files.pythonhosted.org/packages/b8/af/26ce70b42204f923b3babb22f2a5d8c8b651a6b5714ca29b2c4a29155dbb/pulsar_client-3.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:35de0e98fe013e7353d14a87abc62e702dea5accfe434159c1590bf245f5c6fe", size = 7391319, upload-time = "2026-02-05T15:18:38.895Z" }, - { url = "https://files.pythonhosted.org/packages/97/b0/e8892d04e7e120c0406fc5bdfeebba07e92f77a15d7a1caa31e97c8fdabc/pulsar_client-3.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a617e9c83c4fa4c7f3d35d1f3e3c4a2369fa030de7d07aed090a4a09ef7f7ca", size = 7218275, upload-time = "2026-02-05T15:18:42.97Z" }, - { url = "https://files.pythonhosted.org/packages/63/a2/30f16af9efe145ac308ddf9e6c19ed122aba0e69ef8083376ef5525df370/pulsar_client-3.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:e06fd174266521587b83fe81f4b9f5752e23be4e730b1e5097634b56c736c9f7", size = 3702104, upload-time = "2026-02-05T15:18:45.281Z" }, - { url = "https://files.pythonhosted.org/packages/d7/fc/e4d92df3b78d00e170f6d21f03d2404ea492a7da7b0f5ff316c05bd189c8/pulsar_client-3.10.0-cp314-cp314-macosx_13_0_universal2.whl", hash = "sha256:86493bff390605c1dfce91630f894493357151a6474d1043d86eab62e2b1f8b2", size = 9620028, upload-time = "2026-02-05T15:18:49.153Z" }, - { url = "https://files.pythonhosted.org/packages/41/a3/21c3a31518ddaa105e2238643f30aa9e9a008d37b949cac0c3fa2e844c76/pulsar_client-3.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:44838145426263dc28c69e780278e245f3a88a4822a7f4d035cdd7c75f641962", size = 6823648, upload-time = "2026-02-05T15:18:52.623Z" }, - { url = "https://files.pythonhosted.org/packages/1d/7d/87f977d3853ebd4760a1b6130860eaec9b3cc03876b67b1619ac4ded751b/pulsar_client-3.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4ac6d2d33e2e17b24c701bae29689a48c00d0688e8c3ba01ec3f04d5c07ca528", size = 6759349, upload-time = "2026-02-05T15:18:56.114Z" }, - { url = "https://files.pythonhosted.org/packages/d0/42/d6d7941ae1e4e13d5745eb4c1d04029570d0fdec647604d284984e0621ed/pulsar_client-3.10.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64fa8aa9c90e8eae4910198b3dd17b3a0bf3655d1393dd4d65fd51de35ba00ee", size = 7393477, upload-time = "2026-02-05T15:18:59.4Z" }, - { url = "https://files.pythonhosted.org/packages/18/49/915836298132ea9dd25b042ec298cdbbe8eb83ab14c23517cc6c7e60e549/pulsar_client-3.10.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:73f8cb277069d5e21a0715f19dd78b990d4497dba745e495b34395c0b710b23e", size = 7219507, upload-time = "2026-02-05T15:19:02.603Z" }, - { url = "https://files.pythonhosted.org/packages/7c/d0/147929dd08321ef530d8f781204ea98a851f4d177ce0ea4265626370a80c/pulsar_client-3.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:34b689b36521809087f8cb13c00d6f328bac60756ae294e2d55b4197f2886b6b", size = 3817892, upload-time = "2026-02-05T15:19:04.951Z" }, -] - -[[package]] -name = "pyarrow" -version = "23.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/22/134986a4cc224d593c1afde5494d18ff629393d74cc2eddb176669f234a4/pyarrow-23.0.1.tar.gz", hash = "sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019", size = 1167336, upload-time = "2026-02-16T10:14:12.39Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/41/8e6b6ef7e225d4ceead8459427a52afdc23379768f54dd3566014d7618c1/pyarrow-23.0.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb", size = 34302230, upload-time = "2026-02-16T10:09:03.859Z" }, - { url = "https://files.pythonhosted.org/packages/bf/4a/1472c00392f521fea03ae93408bf445cc7bfa1ab81683faf9bc188e36629/pyarrow-23.0.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350", size = 35850050, upload-time = "2026-02-16T10:09:11.877Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b2/bd1f2f05ded56af7f54d702c8364c9c43cd6abb91b0e9933f3d77b4f4132/pyarrow-23.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd", size = 44491918, upload-time = "2026-02-16T10:09:18.144Z" }, - { url = "https://files.pythonhosted.org/packages/0b/62/96459ef5b67957eac38a90f541d1c28833d1b367f014a482cb63f3b7cd2d/pyarrow-23.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9", size = 47562811, upload-time = "2026-02-16T10:09:25.792Z" }, - { url = "https://files.pythonhosted.org/packages/7d/94/1170e235add1f5f45a954e26cd0e906e7e74e23392dcb560de471f7366ec/pyarrow-23.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701", size = 48183766, upload-time = "2026-02-16T10:09:34.645Z" }, - { url = "https://files.pythonhosted.org/packages/0e/2d/39a42af4570377b99774cdb47f63ee6c7da7616bd55b3d5001aa18edfe4f/pyarrow-23.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78", size = 50607669, upload-time = "2026-02-16T10:09:44.153Z" }, - { url = "https://files.pythonhosted.org/packages/00/ca/db94101c187f3df742133ac837e93b1f269ebdac49427f8310ee40b6a58f/pyarrow-23.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919", size = 27527698, upload-time = "2026-02-16T10:09:50.263Z" }, - { url = "https://files.pythonhosted.org/packages/9a/4b/4166bb5abbfe6f750fc60ad337c43ecf61340fa52ab386da6e8dbf9e63c4/pyarrow-23.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f", size = 34214575, upload-time = "2026-02-16T10:09:56.225Z" }, - { url = "https://files.pythonhosted.org/packages/e1/da/3f941e3734ac8088ea588b53e860baeddac8323ea40ce22e3d0baa865cc9/pyarrow-23.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7", size = 35832540, upload-time = "2026-02-16T10:10:03.428Z" }, - { url = "https://files.pythonhosted.org/packages/88/7c/3d841c366620e906d54430817531b877ba646310296df42ef697308c2705/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9", size = 44470940, upload-time = "2026-02-16T10:10:10.704Z" }, - { url = "https://files.pythonhosted.org/packages/2c/a5/da83046273d990f256cb79796a190bbf7ec999269705ddc609403f8c6b06/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05", size = 47586063, upload-time = "2026-02-16T10:10:17.95Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/b7d2ebcff47a514f47f9da1e74b7949138c58cfeb108cdd4ee62f43f0cf3/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67", size = 48173045, upload-time = "2026-02-16T10:10:25.363Z" }, - { url = "https://files.pythonhosted.org/packages/43/b2/b40961262213beaba6acfc88698eb773dfce32ecdf34d19291db94c2bd73/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730", size = 50621741, upload-time = "2026-02-16T10:10:33.477Z" }, - { url = "https://files.pythonhosted.org/packages/f6/70/1fdda42d65b28b078e93d75d371b2185a61da89dda4def8ba6ba41ebdeb4/pyarrow-23.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0", size = 27620678, upload-time = "2026-02-16T10:10:39.31Z" }, - { url = "https://files.pythonhosted.org/packages/47/10/2cbe4c6f0fb83d2de37249567373d64327a5e4d8db72f486db42875b08f6/pyarrow-23.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8", size = 34210066, upload-time = "2026-02-16T10:10:45.487Z" }, - { url = "https://files.pythonhosted.org/packages/cb/4f/679fa7e84dadbaca7a65f7cdba8d6c83febbd93ca12fa4adf40ba3b6362b/pyarrow-23.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f", size = 35825526, upload-time = "2026-02-16T10:10:52.266Z" }, - { url = "https://files.pythonhosted.org/packages/f9/63/d2747d930882c9d661e9398eefc54f15696547b8983aaaf11d4a2e8b5426/pyarrow-23.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677", size = 44473279, upload-time = "2026-02-16T10:11:01.557Z" }, - { url = "https://files.pythonhosted.org/packages/b3/93/10a48b5e238de6d562a411af6467e71e7aedbc9b87f8d3a35f1560ae30fb/pyarrow-23.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2", size = 47585798, upload-time = "2026-02-16T10:11:09.401Z" }, - { url = "https://files.pythonhosted.org/packages/5c/20/476943001c54ef078dbf9542280e22741219a184a0632862bca4feccd666/pyarrow-23.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37", size = 48179446, upload-time = "2026-02-16T10:11:17.781Z" }, - { url = "https://files.pythonhosted.org/packages/4b/b6/5dd0c47b335fcd8edba9bfab78ad961bd0fd55ebe53468cc393f45e0be60/pyarrow-23.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2", size = 50623972, upload-time = "2026-02-16T10:11:26.185Z" }, - { url = "https://files.pythonhosted.org/packages/d5/09/a532297c9591a727d67760e2e756b83905dd89adb365a7f6e9c72578bcc1/pyarrow-23.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a", size = 27540749, upload-time = "2026-02-16T10:12:23.297Z" }, - { url = "https://files.pythonhosted.org/packages/a5/8e/38749c4b1303e6ae76b3c80618f84861ae0c55dd3c2273842ea6f8258233/pyarrow-23.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1", size = 34471544, upload-time = "2026-02-16T10:11:32.535Z" }, - { url = "https://files.pythonhosted.org/packages/a3/73/f237b2bc8c669212f842bcfd842b04fc8d936bfc9d471630569132dc920d/pyarrow-23.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500", size = 35949911, upload-time = "2026-02-16T10:11:39.813Z" }, - { url = "https://files.pythonhosted.org/packages/0c/86/b912195eee0903b5611bf596833def7d146ab2d301afeb4b722c57ffc966/pyarrow-23.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41", size = 44520337, upload-time = "2026-02-16T10:11:47.764Z" }, - { url = "https://files.pythonhosted.org/packages/69/c2/f2a717fb824f62d0be952ea724b4f6f9372a17eed6f704b5c9526f12f2f1/pyarrow-23.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07", size = 47548944, upload-time = "2026-02-16T10:11:56.607Z" }, - { url = "https://files.pythonhosted.org/packages/84/a7/90007d476b9f0dc308e3bc57b832d004f848fd6c0da601375d20d92d1519/pyarrow-23.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83", size = 48236269, upload-time = "2026-02-16T10:12:04.47Z" }, - { url = "https://files.pythonhosted.org/packages/b0/3f/b16fab3e77709856eb6ac328ce35f57a6d4a18462c7ca5186ef31b45e0e0/pyarrow-23.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125", size = 50604794, upload-time = "2026-02-16T10:12:11.797Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a1/22df0620a9fac31d68397a75465c344e83c3dfe521f7612aea33e27ab6c0/pyarrow-23.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8", size = 27660642, upload-time = "2026-02-16T10:12:17.746Z" }, - { url = "https://files.pythonhosted.org/packages/8d/1b/6da9a89583ce7b23ac611f183ae4843cd3a6cf54f079549b0e8c14031e73/pyarrow-23.0.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca", size = 34238755, upload-time = "2026-02-16T10:12:32.819Z" }, - { url = "https://files.pythonhosted.org/packages/ae/b5/d58a241fbe324dbaeb8df07be6af8752c846192d78d2272e551098f74e88/pyarrow-23.0.1-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1", size = 35847826, upload-time = "2026-02-16T10:12:38.949Z" }, - { url = "https://files.pythonhosted.org/packages/54/a5/8cbc83f04aba433ca7b331b38f39e000efd9f0c7ce47128670e737542996/pyarrow-23.0.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb", size = 44536859, upload-time = "2026-02-16T10:12:45.467Z" }, - { url = "https://files.pythonhosted.org/packages/36/2e/c0f017c405fcdc252dbccafbe05e36b0d0eb1ea9a958f081e01c6972927f/pyarrow-23.0.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1", size = 47614443, upload-time = "2026-02-16T10:12:55.525Z" }, - { url = "https://files.pythonhosted.org/packages/af/6b/2314a78057912f5627afa13ba43809d9d653e6630859618b0fd81a4e0759/pyarrow-23.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886", size = 48232991, upload-time = "2026-02-16T10:13:04.729Z" }, - { url = "https://files.pythonhosted.org/packages/40/f2/1bcb1d3be3460832ef3370d621142216e15a2c7c62602a4ea19ec240dd64/pyarrow-23.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f", size = 50645077, upload-time = "2026-02-16T10:13:14.147Z" }, - { url = "https://files.pythonhosted.org/packages/eb/3f/b1da7b61cd66566a4d4c8383d376c606d1c34a906c3f1cb35c479f59d1aa/pyarrow-23.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5", size = 28234271, upload-time = "2026-02-16T10:14:09.397Z" }, - { url = "https://files.pythonhosted.org/packages/b5/78/07f67434e910a0f7323269be7bfbf58699bd0c1d080b18a1ab49ba943fe8/pyarrow-23.0.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d", size = 34488692, upload-time = "2026-02-16T10:13:21.541Z" }, - { url = "https://files.pythonhosted.org/packages/50/76/34cf7ae93ece1f740a04910d9f7e80ba166b9b4ab9596a953e9e62b90fe1/pyarrow-23.0.1-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f", size = 35964383, upload-time = "2026-02-16T10:13:28.63Z" }, - { url = "https://files.pythonhosted.org/packages/46/90/459b827238936d4244214be7c684e1b366a63f8c78c380807ae25ed92199/pyarrow-23.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814", size = 44538119, upload-time = "2026-02-16T10:13:35.506Z" }, - { url = "https://files.pythonhosted.org/packages/28/a1/93a71ae5881e99d1f9de1d4554a87be37da11cd6b152239fb5bd924fdc64/pyarrow-23.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d", size = 47571199, upload-time = "2026-02-16T10:13:42.504Z" }, - { url = "https://files.pythonhosted.org/packages/88/a3/d2c462d4ef313521eaf2eff04d204ac60775263f1fb08c374b543f79f610/pyarrow-23.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7", size = 48259435, upload-time = "2026-02-16T10:13:49.226Z" }, - { url = "https://files.pythonhosted.org/packages/cc/f1/11a544b8c3d38a759eb3fbb022039117fd633e9a7b19e4841cc3da091915/pyarrow-23.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690", size = 50629149, upload-time = "2026-02-16T10:13:57.238Z" }, - { url = "https://files.pythonhosted.org/packages/50/f2/c0e76a0b451ffdf0cf788932e182758eb7558953f4f27f1aff8e2518b653/pyarrow-23.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce", size = 28365807, upload-time = "2026-02-16T10:14:03.892Z" }, -] - -[[package]] -name = "pybase64" -version = "1.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/b8/4ed5c7ad5ec15b08d35cc79ace6145d5c1ae426e46435f4987379439dfea/pybase64-1.4.3.tar.gz", hash = "sha256:c2ed274c9e0ba9c8f9c4083cfe265e66dd679126cd9c2027965d807352f3f053", size = 137272, upload-time = "2025-12-06T13:27:04.013Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/63/21e981e9d3f1f123e0b0ee2130112b1956cad9752309f574862c7ae77c08/pybase64-1.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:70b0d4a4d54e216ce42c2655315378b8903933ecfa32fced453989a92b4317b2", size = 38237, upload-time = "2025-12-06T13:22:52.159Z" }, - { url = "https://files.pythonhosted.org/packages/92/fb/3f448e139516404d2a3963915cc10dc9dde7d3a67de4edba2f827adfef17/pybase64-1.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8127f110cdee7a70e576c5c9c1d4e17e92e76c191869085efbc50419f4ae3c72", size = 31673, upload-time = "2025-12-06T13:22:53.241Z" }, - { url = "https://files.pythonhosted.org/packages/3c/fb/bb06a5b9885e7d853ac1e801c4d8abfdb4c8506deee33e53d55aa6690e67/pybase64-1.4.3-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f9ef0388878bc15a084bd9bf73ec1b2b4ee513d11009b1506375e10a7aae5032", size = 68331, upload-time = "2025-12-06T13:22:54.197Z" }, - { url = "https://files.pythonhosted.org/packages/64/15/8d60b9ec5e658185fc2ee3333e01a6e30d717cf677b24f47cbb3a859d13c/pybase64-1.4.3-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95a57cccf106352a72ed8bc8198f6820b16cc7d55aa3867a16dea7011ae7c218", size = 71370, upload-time = "2025-12-06T13:22:55.517Z" }, - { url = "https://files.pythonhosted.org/packages/ac/29/a3e5c1667cc8c38d025a4636855de0fc117fc62e2afeb033a3c6f12c6a22/pybase64-1.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cd1c47dfceb9c7bd3de210fb4e65904053ed2d7c9dce6d107f041ff6fbd7e21", size = 59834, upload-time = "2025-12-06T13:22:56.682Z" }, - { url = "https://files.pythonhosted.org/packages/a9/00/8ffcf9810bd23f3984698be161cf7edba656fd639b818039a7be1d6405d4/pybase64-1.4.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:9fe9922698f3e2f72874b26890d53a051c431d942701bb3a37aae94da0b12107", size = 56652, upload-time = "2025-12-06T13:22:57.724Z" }, - { url = "https://files.pythonhosted.org/packages/81/62/379e347797cdea4ab686375945bc77ad8d039c688c0d4d0cfb09d247beb9/pybase64-1.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:af5f4bd29c86b59bb4375e0491d16ec8a67548fa99c54763aaedaf0b4b5a6632", size = 59382, upload-time = "2025-12-06T13:22:58.758Z" }, - { url = "https://files.pythonhosted.org/packages/c6/f2/9338ffe2f487086f26a2c8ca175acb3baa86fce0a756ff5670a0822bb877/pybase64-1.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c302f6ca7465262908131411226e02100f488f531bb5e64cb901aa3f439bccd9", size = 59990, upload-time = "2025-12-06T13:23:01.007Z" }, - { url = "https://files.pythonhosted.org/packages/f9/a4/85a6142b65b4df8625b337727aa81dc199642de3d09677804141df6ee312/pybase64-1.4.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2f3f439fa4d7fde164ebbbb41968db7d66b064450ab6017c6c95cef0afa2b349", size = 54923, upload-time = "2025-12-06T13:23:02.369Z" }, - { url = "https://files.pythonhosted.org/packages/ac/00/e40215d25624012bf5b7416ca37f168cb75f6dd15acdb91ea1f2ea4dc4e7/pybase64-1.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a23c6866551043f8b681a5e1e0d59469148b2920a3b4fc42b1275f25ea4217a", size = 58664, upload-time = "2025-12-06T13:23:03.378Z" }, - { url = "https://files.pythonhosted.org/packages/b0/73/d7e19a63e795c13837f2356268d95dc79d1180e756f57ced742a1e52fdeb/pybase64-1.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:56e6526f8565642abc5f84338cc131ce298a8ccab696b19bdf76fa6d7dc592ef", size = 52338, upload-time = "2025-12-06T13:23:04.458Z" }, - { url = "https://files.pythonhosted.org/packages/f2/32/3c746d7a310b69bdd9df77ffc85c41b80bce00a774717596f869b0d4a20e/pybase64-1.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6a792a8b9d866ffa413c9687d9b611553203753987a3a582d68cbc51cf23da45", size = 68993, upload-time = "2025-12-06T13:23:05.526Z" }, - { url = "https://files.pythonhosted.org/packages/5d/b3/63cec68f9d6f6e4c0b438d14e5f1ef536a5fe63ce14b70733ac5e31d7ab8/pybase64-1.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:62ad29a5026bb22cfcd1ca484ec34b0a5ced56ddba38ceecd9359b2818c9c4f9", size = 58055, upload-time = "2025-12-06T13:23:06.931Z" }, - { url = "https://files.pythonhosted.org/packages/d5/cb/7acf7c3c06f9692093c07f109668725dc37fb9a3df0fa912b50add645195/pybase64-1.4.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11b9d1d2d32ec358c02214363b8fc3651f6be7dd84d880ecd597a6206a80e121", size = 54430, upload-time = "2025-12-06T13:23:07.936Z" }, - { url = "https://files.pythonhosted.org/packages/33/39/4eb33ff35d173bfff4002e184ce8907f5d0a42d958d61cd9058ef3570179/pybase64-1.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0aebaa7f238caa0a0d373616016e2040c6c879ebce3ba7ab3c59029920f13640", size = 56272, upload-time = "2025-12-06T13:23:09.253Z" }, - { url = "https://files.pythonhosted.org/packages/19/97/a76d65c375a254e65b730c6f56bf528feca91305da32eceab8bcc08591e6/pybase64-1.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e504682b20c63c2b0c000e5f98a80ea867f8d97642e042a5a39818e44ba4d599", size = 70904, upload-time = "2025-12-06T13:23:10.336Z" }, - { url = "https://files.pythonhosted.org/packages/5e/2c/8338b6d3da3c265002839e92af0a80d6db88385c313c73f103dfb800c857/pybase64-1.4.3-cp311-cp311-win32.whl", hash = "sha256:e9a8b81984e3c6fb1db9e1614341b0a2d98c0033d693d90c726677db1ffa3a4c", size = 33639, upload-time = "2025-12-06T13:23:11.9Z" }, - { url = "https://files.pythonhosted.org/packages/39/dc/32efdf2f5927e5449cc341c266a1bbc5fecd5319a8807d9c5405f76e6d02/pybase64-1.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:a90a8fa16a901fabf20de824d7acce07586e6127dc2333f1de05f73b1f848319", size = 35797, upload-time = "2025-12-06T13:23:13.174Z" }, - { url = "https://files.pythonhosted.org/packages/da/59/eda4f9cb0cbce5a45f0cd06131e710674f8123a4d570772c5b9694f88559/pybase64-1.4.3-cp311-cp311-win_arm64.whl", hash = "sha256:61d87de5bc94d143622e94390ec3e11b9c1d4644fe9be3a81068ab0f91056f59", size = 31160, upload-time = "2025-12-06T13:23:15.696Z" }, - { url = "https://files.pythonhosted.org/packages/86/a7/efcaa564f091a2af7f18a83c1c4875b1437db56ba39540451dc85d56f653/pybase64-1.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:18d85e5ab8b986bb32d8446aca6258ed80d1bafe3603c437690b352c648f5967", size = 38167, upload-time = "2025-12-06T13:23:16.821Z" }, - { url = "https://files.pythonhosted.org/packages/db/c7/c7ad35adff2d272bf2930132db2b3eea8c44bb1b1f64eb9b2b8e57cde7b4/pybase64-1.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3f5791a3491d116d0deaf4d83268f48792998519698f8751efb191eac84320e9", size = 31673, upload-time = "2025-12-06T13:23:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/43/1b/9a8cab0042b464e9a876d5c65fe5127445a2436da36fda64899b119b1a1b/pybase64-1.4.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f0b3f200c3e06316f6bebabd458b4e4bcd4c2ca26af7c0c766614d91968dee27", size = 68210, upload-time = "2025-12-06T13:23:18.813Z" }, - { url = "https://files.pythonhosted.org/packages/62/f7/965b79ff391ad208b50e412b5d3205ccce372a2d27b7218ae86d5295b105/pybase64-1.4.3-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb632edfd132b3eaf90c39c89aa314beec4e946e210099b57d40311f704e11d4", size = 71599, upload-time = "2025-12-06T13:23:20.195Z" }, - { url = "https://files.pythonhosted.org/packages/03/4b/a3b5175130b3810bbb8ccfa1edaadbd3afddb9992d877c8a1e2f274b476e/pybase64-1.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:356ef1d74648ce997f5a777cf8f1aefecc1c0b4fe6201e0ef3ec8a08170e1b54", size = 59922, upload-time = "2025-12-06T13:23:21.487Z" }, - { url = "https://files.pythonhosted.org/packages/da/5d/c38d1572027fc601b62d7a407721688b04b4d065d60ca489912d6893e6cf/pybase64-1.4.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:c48361f90db32bacaa5518419d4eb9066ba558013aaf0c7781620279ecddaeb9", size = 56712, upload-time = "2025-12-06T13:23:22.77Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d4/4e04472fef485caa8f561d904d4d69210a8f8fc1608ea15ebd9012b92655/pybase64-1.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:702bcaa16ae02139d881aeaef5b1c8ffb4a3fae062fe601d1e3835e10310a517", size = 59300, upload-time = "2025-12-06T13:23:24.543Z" }, - { url = "https://files.pythonhosted.org/packages/86/e7/16e29721b86734b881d09b7e23dfd7c8408ad01a4f4c7525f3b1088e25ec/pybase64-1.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:53d0ffe1847b16b647c6413d34d1de08942b7724273dd57e67dcbdb10c574045", size = 60278, upload-time = "2025-12-06T13:23:25.608Z" }, - { url = "https://files.pythonhosted.org/packages/b1/02/18515f211d7c046be32070709a8efeeef8a0203de4fd7521e6b56404731b/pybase64-1.4.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:9a1792e8b830a92736dae58f0c386062eb038dfe8004fb03ba33b6083d89cd43", size = 54817, upload-time = "2025-12-06T13:23:26.633Z" }, - { url = "https://files.pythonhosted.org/packages/e7/be/14e29d8e1a481dbff151324c96dd7b5d2688194bb65dc8a00ca0e1ad1e86/pybase64-1.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1d468b1b1ac5ad84875a46eaa458663c3721e8be5f155ade356406848d3701f6", size = 58611, upload-time = "2025-12-06T13:23:27.684Z" }, - { url = "https://files.pythonhosted.org/packages/b4/8a/a2588dfe24e1bbd742a554553778ab0d65fdf3d1c9a06d10b77047d142aa/pybase64-1.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e97b7bdbd62e71898cd542a6a9e320d9da754ff3ebd02cb802d69087ee94d468", size = 52404, upload-time = "2025-12-06T13:23:28.714Z" }, - { url = "https://files.pythonhosted.org/packages/27/fc/afcda7445bebe0cbc38cafdd7813234cdd4fc5573ff067f1abf317bb0cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b33aeaa780caaa08ffda87fc584d5eab61e3d3bbb5d86ead02161dc0c20d04bc", size = 68817, upload-time = "2025-12-06T13:23:30.079Z" }, - { url = "https://files.pythonhosted.org/packages/d3/3a/87c3201e555ed71f73e961a787241a2438c2bbb2ca8809c29ddf938a3157/pybase64-1.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c0efcf78f11cf866bed49caa7b97552bc4855a892f9cc2372abcd3ed0056f0d", size = 57854, upload-time = "2025-12-06T13:23:31.17Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7d/931c2539b31a7b375e7d595b88401eeb5bd6c5ce1059c9123f9b608aaa14/pybase64-1.4.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:66e3791f2ed725a46593f8bd2761ff37d01e2cdad065b1dceb89066f476e50c6", size = 54333, upload-time = "2025-12-06T13:23:32.422Z" }, - { url = "https://files.pythonhosted.org/packages/de/5e/537601e02cc01f27e9d75f440f1a6095b8df44fc28b1eef2cd739aea8cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:72bb0b6bddadab26e1b069bb78e83092711a111a80a0d6b9edcb08199ad7299b", size = 56492, upload-time = "2025-12-06T13:23:33.515Z" }, - { url = "https://files.pythonhosted.org/packages/96/97/2a2e57acf8f5c9258d22aba52e71f8050e167b29ed2ee1113677c1b600c1/pybase64-1.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5b3365dbcbcdb0a294f0f50af0c0a16b27a232eddeeb0bceeefd844ef30d2a23", size = 70974, upload-time = "2025-12-06T13:23:36.27Z" }, - { url = "https://files.pythonhosted.org/packages/75/2e/a9e28941c6dab6f06e6d3f6783d3373044be9b0f9a9d3492c3d8d2260ac0/pybase64-1.4.3-cp312-cp312-win32.whl", hash = "sha256:7bca1ed3a5df53305c629ca94276966272eda33c0d71f862d2d3d043f1e1b91a", size = 33686, upload-time = "2025-12-06T13:23:37.848Z" }, - { url = "https://files.pythonhosted.org/packages/83/e3/507ab649d8c3512c258819c51d25c45d6e29d9ca33992593059e7b646a33/pybase64-1.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:9f2da8f56d9b891b18b4daf463a0640eae45a80af548ce435be86aa6eff3603b", size = 35833, upload-time = "2025-12-06T13:23:38.877Z" }, - { url = "https://files.pythonhosted.org/packages/bc/8a/6eba66cd549a2fc74bb4425fd61b839ba0ab3022d3c401b8a8dc2cc00c7a/pybase64-1.4.3-cp312-cp312-win_arm64.whl", hash = "sha256:0631d8a2d035de03aa9bded029b9513e1fee8ed80b7ddef6b8e9389ffc445da0", size = 31185, upload-time = "2025-12-06T13:23:39.908Z" }, - { url = "https://files.pythonhosted.org/packages/3a/50/b7170cb2c631944388fe2519507fe3835a4054a6a12a43f43781dae82be1/pybase64-1.4.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:ea4b785b0607d11950b66ce7c328f452614aefc9c6d3c9c28bae795dc7f072e1", size = 33901, upload-time = "2025-12-06T13:23:40.951Z" }, - { url = "https://files.pythonhosted.org/packages/48/8b/69f50578e49c25e0a26e3ee72c39884ff56363344b79fc3967f5af420ed6/pybase64-1.4.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:6a10b6330188c3026a8b9c10e6b9b3f2e445779cf16a4c453d51a072241c65a2", size = 40807, upload-time = "2025-12-06T13:23:42.006Z" }, - { url = "https://files.pythonhosted.org/packages/5c/8d/20b68f11adfc4c22230e034b65c71392e3e338b413bf713c8945bd2ccfb3/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:27fdff227a0c0e182e0ba37a99109645188978b920dfb20d8b9c17eeee370d0d", size = 30932, upload-time = "2025-12-06T13:23:43.348Z" }, - { url = "https://files.pythonhosted.org/packages/f7/79/b1b550ac6bff51a4880bf6e089008b2e1ca16f2c98db5e039a08ac3ad157/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2a8204f1fdfec5aa4184249b51296c0de95445869920c88123978304aad42df1", size = 31394, upload-time = "2025-12-06T13:23:44.317Z" }, - { url = "https://files.pythonhosted.org/packages/82/70/b5d7c5932bf64ee1ec5da859fbac981930b6a55d432a603986c7f509c838/pybase64-1.4.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:874fc2a3777de6baf6aa921a7aa73b3be98295794bea31bd80568a963be30767", size = 38078, upload-time = "2025-12-06T13:23:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/56/fe/e66fe373bce717c6858427670736d54297938dad61c5907517ab4106bd90/pybase64-1.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2dc64a94a9d936b8e3449c66afabbaa521d3cc1a563d6bbaaa6ffa4535222e4b", size = 38158, upload-time = "2025-12-06T13:23:46.872Z" }, - { url = "https://files.pythonhosted.org/packages/80/a9/b806ed1dcc7aed2ea3dd4952286319e6f3a8b48615c8118f453948e01999/pybase64-1.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e48f86de1c145116ccf369a6e11720ce696c2ec02d285f440dfb57ceaa0a6cb4", size = 31672, upload-time = "2025-12-06T13:23:47.88Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c9/24b3b905cf75e23a9a4deaf203b35ffcb9f473ac0e6d8257f91a05dfce62/pybase64-1.4.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1d45c8fe8fe82b65c36b227bb4a2cf623d9ada16bed602ce2d3e18c35285b72a", size = 68244, upload-time = "2025-12-06T13:23:49.026Z" }, - { url = "https://files.pythonhosted.org/packages/f8/cd/d15b0c3e25e5859fab0416dc5b96d34d6bd2603c1c96a07bb2202b68ab92/pybase64-1.4.3-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ad70c26ba091d8f5167e9d4e1e86a0483a5414805cdb598a813db635bd3be8b8", size = 71620, upload-time = "2025-12-06T13:23:50.081Z" }, - { url = "https://files.pythonhosted.org/packages/0d/31/4ca953cc3dcde2b3711d6bfd70a6f4ad2ca95a483c9698076ba605f1520f/pybase64-1.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e98310b7c43145221e7194ac9fa7fffc84763c87bfc5e2f59f9f92363475bdc1", size = 59930, upload-time = "2025-12-06T13:23:51.68Z" }, - { url = "https://files.pythonhosted.org/packages/60/55/e7f7bdcd0fd66e61dda08db158ffda5c89a306bbdaaf5a062fbe4e48f4a1/pybase64-1.4.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:398685a76034e91485a28aeebcb49e64cd663212fd697b2497ac6dfc1df5e671", size = 56425, upload-time = "2025-12-06T13:23:52.732Z" }, - { url = "https://files.pythonhosted.org/packages/cb/65/b592c7f921e51ca1aca3af5b0d201a98666d0a36b930ebb67e7c2ed27395/pybase64-1.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7e46400a6461187ccb52ed75b0045d937529e801a53a9cd770b350509f9e4d50", size = 59327, upload-time = "2025-12-06T13:23:53.856Z" }, - { url = "https://files.pythonhosted.org/packages/23/95/1613d2fb82dbb1548595ad4179f04e9a8451bfa18635efce18b631eabe3f/pybase64-1.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1b62b9f2f291d94f5e0b76ab499790b7dcc78a009d4ceea0b0428770267484b6", size = 60294, upload-time = "2025-12-06T13:23:54.937Z" }, - { url = "https://files.pythonhosted.org/packages/9d/73/40431f37f7d1b3eab4673e7946ff1e8f5d6bd425ec257e834dae8a6fc7b0/pybase64-1.4.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:f30ceb5fa4327809dede614be586efcbc55404406d71e1f902a6fdcf322b93b2", size = 54858, upload-time = "2025-12-06T13:23:56.031Z" }, - { url = "https://files.pythonhosted.org/packages/a7/84/f6368bcaf9f743732e002a9858646fd7a54f428490d427dd6847c5cfe89e/pybase64-1.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0d5f18ed53dfa1d4cf8b39ee542fdda8e66d365940e11f1710989b3cf4a2ed66", size = 58629, upload-time = "2025-12-06T13:23:57.12Z" }, - { url = "https://files.pythonhosted.org/packages/43/75/359532f9adb49c6b546cafc65c46ed75e2ccc220d514ba81c686fbd83965/pybase64-1.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:119d31aa4b58b85a8ebd12b63c07681a138c08dfc2fe5383459d42238665d3eb", size = 52448, upload-time = "2025-12-06T13:23:58.298Z" }, - { url = "https://files.pythonhosted.org/packages/92/6c/ade2ba244c3f33ed920a7ed572ad772eb0b5f14480b72d629d0c9e739a40/pybase64-1.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3cf0218b0e2f7988cf7d738a73b6a1d14f3be6ce249d7c0f606e768366df2cce", size = 68841, upload-time = "2025-12-06T13:23:59.886Z" }, - { url = "https://files.pythonhosted.org/packages/a0/51/b345139cd236be382f2d4d4453c21ee6299e14d2f759b668e23080f8663f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:12f4ee5e988bc5c0c1106b0d8fc37fb0508f12dab76bac1b098cb500d148da9d", size = 57910, upload-time = "2025-12-06T13:24:00.994Z" }, - { url = "https://files.pythonhosted.org/packages/1a/b8/9f84bdc4f1c4f0052489396403c04be2f9266a66b70c776001eaf0d78c1f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:937826bc7b6b95b594a45180e81dd4d99bd4dd4814a443170e399163f7ff3fb6", size = 54335, upload-time = "2025-12-06T13:24:02.046Z" }, - { url = "https://files.pythonhosted.org/packages/d0/c7/be63b617d284de46578a366da77ede39c8f8e815ed0d82c7c2acca560fab/pybase64-1.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:88995d1460971ef80b13e3e007afbe4b27c62db0508bc7250a2ab0a0b4b91362", size = 56486, upload-time = "2025-12-06T13:24:03.141Z" }, - { url = "https://files.pythonhosted.org/packages/5e/96/f252c8f9abd6ded3ef1ccd3cdbb8393a33798007f761b23df8de1a2480e6/pybase64-1.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:72326fe163385ed3e1e806dd579d47fde5d8a59e51297a60fc4e6cbc1b4fc4ed", size = 70978, upload-time = "2025-12-06T13:24:04.221Z" }, - { url = "https://files.pythonhosted.org/packages/af/51/0f5714af7aeef96e30f968e4371d75ad60558aaed3579d7c6c8f1c43c18a/pybase64-1.4.3-cp313-cp313-win32.whl", hash = "sha256:b1623730c7892cf5ed0d6355e375416be6ef8d53ab9b284f50890443175c0ac3", size = 33684, upload-time = "2025-12-06T13:24:05.29Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ad/0cea830a654eb08563fb8214150ef57546ece1cc421c09035f0e6b0b5ea9/pybase64-1.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:8369887590f1646a5182ca2fb29252509da7ae31d4923dbb55d3e09da8cc4749", size = 35832, upload-time = "2025-12-06T13:24:06.35Z" }, - { url = "https://files.pythonhosted.org/packages/b4/0d/eec2a8214989c751bc7b4cad1860eb2c6abf466e76b77508c0f488c96a37/pybase64-1.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:860b86bca71e5f0237e2ab8b2d9c4c56681f3513b1bf3e2117290c1963488390", size = 31175, upload-time = "2025-12-06T13:24:07.419Z" }, - { url = "https://files.pythonhosted.org/packages/db/c9/e23463c1a2913686803ef76b1a5ae7e6fac868249a66e48253d17ad7232c/pybase64-1.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eb51db4a9c93215135dccd1895dca078e8785c357fabd983c9f9a769f08989a9", size = 38497, upload-time = "2025-12-06T13:24:08.873Z" }, - { url = "https://files.pythonhosted.org/packages/71/83/343f446b4b7a7579bf6937d2d013d82f1a63057cf05558e391ab6039d7db/pybase64-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a03ef3f529d85fd46b89971dfb00c634d53598d20ad8908fb7482955c710329d", size = 32076, upload-time = "2025-12-06T13:24:09.975Z" }, - { url = "https://files.pythonhosted.org/packages/46/fc/cb64964c3b29b432f54d1bce5e7691d693e33bbf780555151969ffd95178/pybase64-1.4.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2e745f2ce760c6cf04d8a72198ef892015ddb89f6ceba489e383518ecbdb13ab", size = 72317, upload-time = "2025-12-06T13:24:11.129Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b7/fab2240da6f4e1ad46f71fa56ec577613cf5df9dce2d5b4cfaa4edd0e365/pybase64-1.4.3-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fac217cd9de8581a854b0ac734c50fd1fa4b8d912396c1fc2fce7c230efe3a7", size = 75534, upload-time = "2025-12-06T13:24:12.433Z" }, - { url = "https://files.pythonhosted.org/packages/91/3b/3e2f2b6e68e3d83ddb9fa799f3548fb7449765daec9bbd005a9fbe296d7f/pybase64-1.4.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:da1ee8fa04b283873de2d6e8fa5653e827f55b86bdf1a929c5367aaeb8d26f8a", size = 65399, upload-time = "2025-12-06T13:24:13.928Z" }, - { url = "https://files.pythonhosted.org/packages/6b/08/476ac5914c3b32e0274a2524fc74f01cbf4f4af4513d054e41574eb018f6/pybase64-1.4.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:b0bf8e884ee822ca7b1448eeb97fa131628fe0ff42f60cae9962789bd562727f", size = 60487, upload-time = "2025-12-06T13:24:15.177Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b8/618a92915330cc9cba7880299b546a1d9dab1a21fd6c0292ee44a4fe608c/pybase64-1.4.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1bf749300382a6fd1f4f255b183146ef58f8e9cb2f44a077b3a9200dfb473a77", size = 63959, upload-time = "2025-12-06T13:24:16.854Z" }, - { url = "https://files.pythonhosted.org/packages/a5/52/af9d8d051652c3051862c442ec3861259c5cdb3fc69774bc701470bd2a59/pybase64-1.4.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:153a0e42329b92337664cfc356f2065248e6c9a1bd651bbcd6dcaf15145d3f06", size = 64874, upload-time = "2025-12-06T13:24:18.328Z" }, - { url = "https://files.pythonhosted.org/packages/e4/51/5381a7adf1f381bd184d33203692d3c57cf8ae9f250f380c3fecbdbe554b/pybase64-1.4.3-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:86ee56ac7f2184ca10217ed1c655c1a060273e233e692e9086da29d1ae1768db", size = 58572, upload-time = "2025-12-06T13:24:19.417Z" }, - { url = "https://files.pythonhosted.org/packages/e0/f0/578ee4ffce5818017de4fdf544e066c225bc435e73eb4793cde28a689d0b/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0e71a4db76726bf830b47477e7d830a75c01b2e9b01842e787a0836b0ba741e3", size = 63636, upload-time = "2025-12-06T13:24:20.497Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ad/8ae94814bf20159ea06310b742433e53d5820aa564c9fdf65bf2d79f8799/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2ba7799ec88540acd9861b10551d24656ca3c2888ecf4dba2ee0a71544a8923f", size = 56193, upload-time = "2025-12-06T13:24:21.559Z" }, - { url = "https://files.pythonhosted.org/packages/d1/31/6438cfcc3d3f0fa84d229fa125c243d5094e72628e525dfefadf3bcc6761/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2860299e4c74315f5951f0cf3e72ba0f201c3356c8a68f95a3ab4e620baf44e9", size = 72655, upload-time = "2025-12-06T13:24:22.673Z" }, - { url = "https://files.pythonhosted.org/packages/a3/0d/2bbc9e9c3fc12ba8a6e261482f03a544aca524f92eae0b4908c0a10ba481/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:bb06015db9151f0c66c10aae8e3603adab6b6cd7d1f7335a858161d92fc29618", size = 62471, upload-time = "2025-12-06T13:24:23.8Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0b/34d491e7f49c1dbdb322ea8da6adecda7c7cd70b6644557c6e4ca5c6f7c7/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:242512a070817272865d37c8909059f43003b81da31f616bb0c391ceadffe067", size = 58119, upload-time = "2025-12-06T13:24:24.994Z" }, - { url = "https://files.pythonhosted.org/packages/ce/17/c21d0cde2a6c766923ae388fc1f78291e1564b0d38c814b5ea8a0e5e081c/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5d8277554a12d3e3eed6180ebda62786bf9fc8d7bb1ee00244258f4a87ca8d20", size = 60791, upload-time = "2025-12-06T13:24:26.046Z" }, - { url = "https://files.pythonhosted.org/packages/92/b2/eaa67038916a48de12b16f4c384bcc1b84b7ec731b23613cb05f27673294/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f40b7ddd698fc1e13a4b64fbe405e4e0e1279e8197e37050e24154655f5f7c4e", size = 74701, upload-time = "2025-12-06T13:24:27.466Z" }, - { url = "https://files.pythonhosted.org/packages/42/10/abb7757c330bb869ebb95dab0c57edf5961ffbd6c095c8209cbbf75d117d/pybase64-1.4.3-cp313-cp313t-win32.whl", hash = "sha256:46d75c9387f354c5172582a9eaae153b53a53afeb9c19fcf764ea7038be3bd8b", size = 33965, upload-time = "2025-12-06T13:24:28.548Z" }, - { url = "https://files.pythonhosted.org/packages/63/a0/2d4e5a59188e9e6aed0903d580541aaea72dcbbab7bf50fb8b83b490b6c3/pybase64-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:d7344625591d281bec54e85cbfdab9e970f6219cac1570f2aa140b8c942ccb81", size = 36207, upload-time = "2025-12-06T13:24:29.646Z" }, - { url = "https://files.pythonhosted.org/packages/1f/05/95b902e8f567b4d4b41df768ccc438af618f8d111e54deaf57d2df46bd76/pybase64-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:28a3c60c55138e0028313f2eccd321fec3c4a0be75e57a8d3eb883730b1b0880", size = 31505, upload-time = "2025-12-06T13:24:30.687Z" }, - { url = "https://files.pythonhosted.org/packages/e4/80/4bd3dff423e5a91f667ca41982dc0b79495b90ec0c0f5d59aca513e50f8c/pybase64-1.4.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:015bb586a1ea1467f69d57427abe587469392215f59db14f1f5c39b52fdafaf5", size = 33835, upload-time = "2025-12-06T13:24:31.767Z" }, - { url = "https://files.pythonhosted.org/packages/45/60/a94d94cc1e3057f602e0b483c9ebdaef40911d84a232647a2fe593ab77bb/pybase64-1.4.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:d101e3a516f837c3dcc0e5a0b7db09582ebf99ed670865223123fb2e5839c6c0", size = 40673, upload-time = "2025-12-06T13:24:32.82Z" }, - { url = "https://files.pythonhosted.org/packages/e3/71/cf62b261d431857e8e054537a5c3c24caafa331de30daede7b2c6c558501/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8f183ac925a48046abe047360fe3a1b28327afb35309892132fe1915d62fb282", size = 30939, upload-time = "2025-12-06T13:24:34.001Z" }, - { url = "https://files.pythonhosted.org/packages/24/3e/d12f92a3c1f7c6ab5d53c155bff9f1084ba997a37a39a4f781ccba9455f3/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30bf3558e24dcce4da5248dcf6d73792adfcf4f504246967e9db155be4c439ad", size = 31401, upload-time = "2025-12-06T13:24:35.11Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3d/9c27440031fea0d05146f8b70a460feb95d8b4e3d9ca8f45c972efb4c3d3/pybase64-1.4.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a674b419de318d2ce54387dd62646731efa32b4b590907800f0bd40675c1771d", size = 38075, upload-time = "2025-12-06T13:24:36.53Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d4/6c0e0cf0efd53c254173fbcd84a3d8fcbf5e0f66622473da425becec32a5/pybase64-1.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:720104fd7303d07bac302be0ff8f7f9f126f2f45c1edb4f48fdb0ff267e69fe1", size = 38257, upload-time = "2025-12-06T13:24:38.049Z" }, - { url = "https://files.pythonhosted.org/packages/50/eb/27cb0b610d5cd70f5ad0d66c14ad21c04b8db930f7139818e8fbdc14df4d/pybase64-1.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:83f1067f73fa5afbc3efc0565cecc6ed53260eccddef2ebe43a8ce2b99ea0e0a", size = 31685, upload-time = "2025-12-06T13:24:40.327Z" }, - { url = "https://files.pythonhosted.org/packages/db/26/b136a4b65e5c94ff06217f7726478df3f31ab1c777c2c02cf698e748183f/pybase64-1.4.3-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b51204d349a4b208287a8aa5b5422be3baa88abf6cc8ff97ccbda34919bbc857", size = 68460, upload-time = "2025-12-06T13:24:41.735Z" }, - { url = "https://files.pythonhosted.org/packages/68/6d/84ce50e7ee1ae79984d689e05a9937b2460d4efa1e5b202b46762fb9036c/pybase64-1.4.3-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:30f2fd53efecbdde4bdca73a872a68dcb0d1bf8a4560c70a3e7746df973e1ef3", size = 71688, upload-time = "2025-12-06T13:24:42.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/57/6743e420416c3ff1b004041c85eb0ebd9c50e9cf05624664bfa1dc8b5625/pybase64-1.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0932b0c5cfa617091fd74f17d24549ce5de3628791998c94ba57be808078eeaf", size = 60040, upload-time = "2025-12-06T13:24:44.37Z" }, - { url = "https://files.pythonhosted.org/packages/3b/68/733324e28068a89119af2921ce548e1c607cc5c17d354690fc51c302e326/pybase64-1.4.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:acb61f5ab72bec808eb0d4ce8b87ec9f38d7d750cb89b1371c35eb8052a29f11", size = 56478, upload-time = "2025-12-06T13:24:45.815Z" }, - { url = "https://files.pythonhosted.org/packages/b5/9e/f3f4aa8cfe3357a3cdb0535b78eb032b671519d3ecc08c58c4c6b72b5a91/pybase64-1.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:2bc2d5bc15168f5c04c53bdfe5a1e543b2155f456ed1e16d7edce9ce73842021", size = 59463, upload-time = "2025-12-06T13:24:46.938Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d1/53286038e1f0df1cf58abcf4a4a91b0f74ab44539c2547b6c31001ddd054/pybase64-1.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:8a7bc3cd23880bdca59758bcdd6f4ef0674f2393782763910a7466fab35ccb98", size = 60360, upload-time = "2025-12-06T13:24:48.039Z" }, - { url = "https://files.pythonhosted.org/packages/00/9a/5cc6ce95db2383d27ff4d790b8f8b46704d360d701ab77c4f655bcfaa6a7/pybase64-1.4.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ad15acf618880d99792d71e3905b0e2508e6e331b76a1b34212fa0f11e01ad28", size = 54999, upload-time = "2025-12-06T13:24:49.547Z" }, - { url = "https://files.pythonhosted.org/packages/64/e7/c3c1d09c3d7ae79e3aa1358c6d912d6b85f29281e47aa94fc0122a415a2f/pybase64-1.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448158d417139cb4851200e5fee62677ae51f56a865d50cda9e0d61bda91b116", size = 58736, upload-time = "2025-12-06T13:24:50.641Z" }, - { url = "https://files.pythonhosted.org/packages/db/d5/0baa08e3d8119b15b588c39f0d39fd10472f0372e3c54ca44649cbefa256/pybase64-1.4.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9058c49b5a2f3e691b9db21d37eb349e62540f9f5fc4beabf8cbe3c732bead86", size = 52298, upload-time = "2025-12-06T13:24:51.791Z" }, - { url = "https://files.pythonhosted.org/packages/00/87/fc6f11474a1de7e27cd2acbb8d0d7508bda3efa73dfe91c63f968728b2a3/pybase64-1.4.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ce561724f6522907a66303aca27dce252d363fcd85884972d348f4403ba3011a", size = 69049, upload-time = "2025-12-06T13:24:53.253Z" }, - { url = "https://files.pythonhosted.org/packages/69/9d/7fb5566f669ac18b40aa5fc1c438e24df52b843c1bdc5da47d46d4c1c630/pybase64-1.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:63316560a94ac449fe86cb8b9e0a13714c659417e92e26a5cbf085cd0a0c838d", size = 57952, upload-time = "2025-12-06T13:24:54.342Z" }, - { url = "https://files.pythonhosted.org/packages/de/cc/ceb949232dbbd3ec4ee0190d1df4361296beceee9840390a63df8bc31784/pybase64-1.4.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7ecd796f2ac0be7b73e7e4e232b8c16422014de3295d43e71d2b19fd4a4f5368", size = 54484, upload-time = "2025-12-06T13:24:55.774Z" }, - { url = "https://files.pythonhosted.org/packages/a7/69/659f3c8e6a5d7b753b9c42a4bd9c42892a0f10044e9c7351a4148d413a33/pybase64-1.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d01e102a12fb2e1ed3dc11611c2818448626637857ec3994a9cf4809dfd23477", size = 56542, upload-time = "2025-12-06T13:24:57Z" }, - { url = "https://files.pythonhosted.org/packages/85/2c/29c9e6c9c82b72025f9676f9e82eb1fd2339ad038cbcbf8b9e2ac02798fc/pybase64-1.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ebff797a93c2345f22183f454fd8607a34d75eca5a3a4a969c1c75b304cee39d", size = 71045, upload-time = "2025-12-06T13:24:58.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/84/5a3dce8d7a0040a5c0c14f0fe1311cd8db872913fa04438071b26b0dac04/pybase64-1.4.3-cp314-cp314-win32.whl", hash = "sha256:28b2a1bb0828c0595dc1ea3336305cd97ff85b01c00d81cfce4f92a95fb88f56", size = 34200, upload-time = "2025-12-06T13:24:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/57/bc/ce7427c12384adee115b347b287f8f3cf65860b824d74fe2c43e37e81c1f/pybase64-1.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:33338d3888700ff68c3dedfcd49f99bfc3b887570206130926791e26b316b029", size = 36323, upload-time = "2025-12-06T13:25:01.708Z" }, - { url = "https://files.pythonhosted.org/packages/9a/1b/2b8ffbe9a96eef7e3f6a5a7be75995eebfb6faaedc85b6da6b233e50c778/pybase64-1.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:62725669feb5acb186458da2f9353e88ae28ef66bb9c4c8d1568b12a790dfa94", size = 31584, upload-time = "2025-12-06T13:25:02.801Z" }, - { url = "https://files.pythonhosted.org/packages/ac/d8/6824c2e6fb45b8fa4e7d92e3c6805432d5edc7b855e3e8e1eedaaf6efb7c/pybase64-1.4.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:153fe29be038948d9372c3e77ae7d1cab44e4ba7d9aaf6f064dbeea36e45b092", size = 38601, upload-time = "2025-12-06T13:25:04.222Z" }, - { url = "https://files.pythonhosted.org/packages/ea/e5/10d2b3a4ad3a4850be2704a2f70cd9c0cf55725c8885679872d3bc846c67/pybase64-1.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f7fe3decaa7c4a9e162327ec7bd81ce183d2b16f23c6d53b606649c6e0203e9e", size = 32078, upload-time = "2025-12-06T13:25:05.362Z" }, - { url = "https://files.pythonhosted.org/packages/43/04/8b15c34d3c2282f1c1b0850f1113a249401b618a382646a895170bc9b5e7/pybase64-1.4.3-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a5ae04ea114c86eb1da1f6e18d75f19e3b5ae39cb1d8d3cd87c29751a6a22780", size = 72474, upload-time = "2025-12-06T13:25:06.434Z" }, - { url = "https://files.pythonhosted.org/packages/42/00/f34b4d11278f8fdc68bc38f694a91492aa318f7c6f1bd7396197ac0f8b12/pybase64-1.4.3-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1755b3dce3a2a5c7d17ff6d4115e8bee4a1d5aeae74469db02e47c8f477147da", size = 75706, upload-time = "2025-12-06T13:25:07.636Z" }, - { url = "https://files.pythonhosted.org/packages/bb/5d/71747d4ad7fe16df4c4c852bdbdeb1f2cf35677b48d7c34d3011a7a6ad3a/pybase64-1.4.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb852f900e27ffc4ec1896817535a0fa19610ef8875a096b59f21d0aa42ff172", size = 65589, upload-time = "2025-12-06T13:25:08.809Z" }, - { url = "https://files.pythonhosted.org/packages/49/b1/d1e82bd58805bb5a3a662864800bab83a83a36ba56e7e3b1706c708002a5/pybase64-1.4.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:9cf21ea8c70c61eddab3421fbfce061fac4f2fb21f7031383005a1efdb13d0b9", size = 60670, upload-time = "2025-12-06T13:25:10.04Z" }, - { url = "https://files.pythonhosted.org/packages/15/67/16c609b7a13d1d9fc87eca12ba2dce5e67f949eeaab61a41bddff843cbb0/pybase64-1.4.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:afff11b331fdc27692fc75e85ae083340a35105cea1a3c4552139e2f0e0d174f", size = 64194, upload-time = "2025-12-06T13:25:11.48Z" }, - { url = "https://files.pythonhosted.org/packages/3c/11/37bc724e42960f0106c2d33dc957dcec8f760c91a908cc6c0df7718bc1a8/pybase64-1.4.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9a5143df542c1ce5c1f423874b948c4d689b3f05ec571f8792286197a39ba02", size = 64984, upload-time = "2025-12-06T13:25:12.645Z" }, - { url = "https://files.pythonhosted.org/packages/6e/66/b2b962a6a480dd5dae3029becf03ea1a650d326e39bf1c44ea3db78bb010/pybase64-1.4.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:d62e9861019ad63624b4a7914dff155af1cc5d6d79df3be14edcaedb5fdad6f9", size = 58750, upload-time = "2025-12-06T13:25:13.848Z" }, - { url = "https://files.pythonhosted.org/packages/2b/15/9b6d711035e29b18b2e1c03d47f41396d803d06ef15b6c97f45b75f73f04/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:84cfd4d92668ef5766cc42a9c9474b88960ac2b860767e6e7be255c6fddbd34a", size = 63816, upload-time = "2025-12-06T13:25:15.356Z" }, - { url = "https://files.pythonhosted.org/packages/b4/21/e2901381ed0df62e2308380f30d9c4d87d6b74e33a84faed3478d33a7197/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:60fc025437f9a7c2cc45e0c19ed68ed08ba672be2c5575fd9d98bdd8f01dd61f", size = 56348, upload-time = "2025-12-06T13:25:16.559Z" }, - { url = "https://files.pythonhosted.org/packages/c4/16/3d788388a178a0407aa814b976fe61bfa4af6760d9aac566e59da6e4a8b4/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:edc8446196f04b71d3af76c0bd1fe0a45066ac5bffecca88adb9626ee28c266f", size = 72842, upload-time = "2025-12-06T13:25:18.055Z" }, - { url = "https://files.pythonhosted.org/packages/a6/63/c15b1f8bd47ea48a5a2d52a4ec61f037062932ea6434ab916107b58e861e/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e99f6fa6509c037794da57f906ade271f52276c956d00f748e5b118462021d48", size = 62651, upload-time = "2025-12-06T13:25:19.191Z" }, - { url = "https://files.pythonhosted.org/packages/bd/b8/f544a2e37c778d59208966d4ef19742a0be37c12fc8149ff34483c176616/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d94020ef09f624d841aa9a3a6029df8cf65d60d7a6d5c8687579fa68bd679b65", size = 58295, upload-time = "2025-12-06T13:25:20.822Z" }, - { url = "https://files.pythonhosted.org/packages/03/99/1fae8a3b7ac181e36f6e7864a62d42d5b1f4fa7edf408c6711e28fba6b4d/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f64ce70d89942a23602dee910dec9b48e5edf94351e1b378186b74fcc00d7f66", size = 60960, upload-time = "2025-12-06T13:25:22.099Z" }, - { url = "https://files.pythonhosted.org/packages/9d/9e/cd4c727742345ad8384569a4466f1a1428f4e5cc94d9c2ab2f53d30be3fe/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8ea99f56e45c469818b9781903be86ba4153769f007ba0655fa3b46dc332803d", size = 74863, upload-time = "2025-12-06T13:25:23.442Z" }, - { url = "https://files.pythonhosted.org/packages/28/86/a236ecfc5b494e1e922da149689f690abc84248c7c1358f5605b8c9fdd60/pybase64-1.4.3-cp314-cp314t-win32.whl", hash = "sha256:343b1901103cc72362fd1f842524e3bb24978e31aea7ff11e033af7f373f66ab", size = 34513, upload-time = "2025-12-06T13:25:24.592Z" }, - { url = "https://files.pythonhosted.org/packages/56/ce/ca8675f8d1352e245eb012bfc75429ee9cf1f21c3256b98d9a329d44bf0f/pybase64-1.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:57aff6f7f9dea6705afac9d706432049642de5b01080d3718acc23af87c5af76", size = 36702, upload-time = "2025-12-06T13:25:25.72Z" }, - { url = "https://files.pythonhosted.org/packages/3b/30/4a675864877397179b09b720ee5fcb1cf772cf7bebc831989aff0a5f79c1/pybase64-1.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:e906aa08d4331e799400829e0f5e4177e76a3281e8a4bc82ba114c6b30e405c9", size = 31904, upload-time = "2025-12-06T13:25:26.826Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7c/545fd4935a0e1ddd7147f557bf8157c73eecec9cffd523382fa7af2557de/pybase64-1.4.3-graalpy311-graalpy242_311_native-macosx_10_9_x86_64.whl", hash = "sha256:d27c1dfdb0c59a5e758e7a98bd78eaca5983c22f4a811a36f4f980d245df4611", size = 38393, upload-time = "2025-12-06T13:26:19.535Z" }, - { url = "https://files.pythonhosted.org/packages/c3/ca/ae7a96be9ddc96030d4e9dffc43635d4e136b12058b387fd47eb8301b60f/pybase64-1.4.3-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0f1a0c51d6f159511e3431b73c25db31095ee36c394e26a4349e067c62f434e5", size = 32109, upload-time = "2025-12-06T13:26:20.72Z" }, - { url = "https://files.pythonhosted.org/packages/bf/44/d4b7adc7bf4fd5b52d8d099121760c450a52c390223806b873f0b6a2d551/pybase64-1.4.3-graalpy311-graalpy242_311_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a492518f3078a4e3faaef310697d21df9c6bc71908cebc8c2f6fbfa16d7d6b1f", size = 43227, upload-time = "2025-12-06T13:26:21.845Z" }, - { url = "https://files.pythonhosted.org/packages/08/86/2ba2d8734ef7939debeb52cf9952e457ba7aa226cae5c0e6dd631f9b851f/pybase64-1.4.3-graalpy311-graalpy242_311_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cae1a0f47784fd16df90d8acc32011c8d5fcdd9ab392c9ec49543e5f6a9c43a4", size = 35804, upload-time = "2025-12-06T13:26:23.149Z" }, - { url = "https://files.pythonhosted.org/packages/4f/5b/19c725dc3aaa6281f2ce3ea4c1628d154a40dd99657d1381995f8096768b/pybase64-1.4.3-graalpy311-graalpy242_311_native-win_amd64.whl", hash = "sha256:03cea70676ffbd39a1ab7930a2d24c625b416cacc9d401599b1d29415a43ab6a", size = 35880, upload-time = "2025-12-06T13:26:24.663Z" }, - { url = "https://files.pythonhosted.org/packages/17/45/92322aec1b6979e789b5710f73c59f2172bc37c8ce835305434796824b7b/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:2baaa092f3475f3a9c87ac5198023918ea8b6c125f4c930752ab2cbe3cd1d520", size = 38746, upload-time = "2025-12-06T13:26:25.869Z" }, - { url = "https://files.pythonhosted.org/packages/11/94/f1a07402870388fdfc2ecec0c718111189732f7d0f2d7fe1386e19e8fad0/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:cde13c0764b1af07a631729f26df019070dad759981d6975527b7e8ecb465b6c", size = 32573, upload-time = "2025-12-06T13:26:27.792Z" }, - { url = "https://files.pythonhosted.org/packages/fa/8f/43c3bb11ca9bacf81cb0b7a71500bb65b2eda6d5fe07433c09b543de97f3/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5c29a582b0ea3936d02bd6fe9bf674ab6059e6e45ab71c78404ab2c913224414", size = 43461, upload-time = "2025-12-06T13:26:28.906Z" }, - { url = "https://files.pythonhosted.org/packages/2d/4c/2a5258329200be57497d3972b5308558c6de42e3749c6cc2aa1cbe34b25a/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6b664758c804fa919b4f1257aa8cf68e95db76fc331de5f70bfc3a34655afe1", size = 36058, upload-time = "2025-12-06T13:26:30.092Z" }, - { url = "https://files.pythonhosted.org/packages/ea/6d/41faa414cde66ec023b0ca8402a8f11cb61731c3dc27c082909cbbd1f929/pybase64-1.4.3-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:f7537fa22ae56a0bf51e4b0ffc075926ad91c618e1416330939f7ef366b58e3b", size = 36231, upload-time = "2025-12-06T13:26:31.656Z" }, - { url = "https://files.pythonhosted.org/packages/b2/76/160dded493c00d3376d4ad0f38a2119c5345de4a6693419ad39c3565959b/pybase64-1.4.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:277de6e03cc9090fb359365c686a2a3036d23aee6cd20d45d22b8c89d1247f17", size = 37939, upload-time = "2025-12-06T13:26:41.014Z" }, - { url = "https://files.pythonhosted.org/packages/b7/b8/a0f10be8d648d6f8f26e560d6e6955efa7df0ff1e009155717454d76f601/pybase64-1.4.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ab1dd8b1ed2d1d750260ed58ab40defaa5ba83f76a30e18b9ebd5646f6247ae5", size = 31466, upload-time = "2025-12-06T13:26:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/d3/22/832a2f9e76cdf39b52e01e40d8feeb6a04cf105494f2c3e3126d0149717f/pybase64-1.4.3-pp311-pypy311_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:bd4d2293de9fd212e294c136cec85892460b17d24e8c18a6ba18750928037750", size = 40681, upload-time = "2025-12-06T13:26:43.782Z" }, - { url = "https://files.pythonhosted.org/packages/12/d7/6610f34a8972415fab3bb4704c174a1cc477bffbc3c36e526428d0f3957d/pybase64-1.4.3-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af6d0d3a691911cc4c9a625f3ddcd3af720738c21be3d5c72de05629139d393", size = 41294, upload-time = "2025-12-06T13:26:44.936Z" }, - { url = "https://files.pythonhosted.org/packages/64/25/ed24400948a6c974ab1374a233cb7e8af0a5373cea0dd8a944627d17c34a/pybase64-1.4.3-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5cfc8c49a28322d82242088378f8542ce97459866ba73150b062a7073e82629d", size = 35447, upload-time = "2025-12-06T13:26:46.098Z" }, - { url = "https://files.pythonhosted.org/packages/ee/2b/e18ee7c5ee508a82897f021c1981533eca2940b5f072fc6ed0906c03a7a7/pybase64-1.4.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:debf737e09b8bf832ba86f5ecc3d3dbd0e3021d6cd86ba4abe962d6a5a77adb3", size = 36134, upload-time = "2025-12-06T13:26:47.35Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pycryptodome" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/5d/bdb09489b63cd34a976cc9e2a8d938114f7a53a74d3dd4f125ffa49dce82/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4", size = 2495152, upload-time = "2025-05-17T17:20:20.833Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ce/7840250ed4cc0039c433cd41715536f926d6e86ce84e904068eb3244b6a6/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae", size = 1639348, upload-time = "2025-05-17T17:20:23.171Z" }, - { url = "https://files.pythonhosted.org/packages/ee/f0/991da24c55c1f688d6a3b5a11940567353f74590734ee4a64294834ae472/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477", size = 2184033, upload-time = "2025-05-17T17:20:25.424Z" }, - { url = "https://files.pythonhosted.org/packages/54/16/0e11882deddf00f68b68dd4e8e442ddc30641f31afeb2bc25588124ac8de/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7", size = 2270142, upload-time = "2025-05-17T17:20:27.808Z" }, - { url = "https://files.pythonhosted.org/packages/d5/fc/4347fea23a3f95ffb931f383ff28b3f7b1fe868739182cb76718c0da86a1/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446", size = 2309384, upload-time = "2025-05-17T17:20:30.765Z" }, - { url = "https://files.pythonhosted.org/packages/6e/d9/c5261780b69ce66d8cfab25d2797bd6e82ba0241804694cd48be41add5eb/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265", size = 2183237, upload-time = "2025-05-17T17:20:33.736Z" }, - { url = "https://files.pythonhosted.org/packages/5a/6f/3af2ffedd5cfa08c631f89452c6648c4d779e7772dfc388c77c920ca6bbf/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b", size = 2343898, upload-time = "2025-05-17T17:20:36.086Z" }, - { url = "https://files.pythonhosted.org/packages/9a/dc/9060d807039ee5de6e2f260f72f3d70ac213993a804f5e67e0a73a56dd2f/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d", size = 2269197, upload-time = "2025-05-17T17:20:38.414Z" }, - { url = "https://files.pythonhosted.org/packages/f9/34/e6c8ca177cb29dcc4967fef73f5de445912f93bd0343c9c33c8e5bf8cde8/pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a", size = 1768600, upload-time = "2025-05-17T17:20:40.688Z" }, - { url = "https://files.pythonhosted.org/packages/e4/1d/89756b8d7ff623ad0160f4539da571d1f594d21ee6d68be130a6eccb39a4/pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625", size = 1799740, upload-time = "2025-05-17T17:20:42.413Z" }, - { url = "https://files.pythonhosted.org/packages/5d/61/35a64f0feaea9fd07f0d91209e7be91726eb48c0f1bfc6720647194071e4/pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39", size = 1703685, upload-time = "2025-05-17T17:20:44.388Z" }, - { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" }, - { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" }, - { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" }, - { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" }, - { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" }, - { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" }, - { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" }, - { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" }, -] - -[[package]] -name = "pydantic" -version = "1.10.26" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/da/fd89f987a376c807cd81ea0eff4589aade783bbb702637b4734ef2c743a2/pydantic-1.10.26.tar.gz", hash = "sha256:8c6aa39b494c5af092e690127c283d84f363ac36017106a9e66cb33a22ac412e", size = 357906, upload-time = "2025-12-18T15:47:46.557Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/c1/d521e64c8130e1ad9d22c270bed3fabcc0940c9539b076b639c88fd32a8d/pydantic-1.10.26-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:116233e53889bcc536f617e38c1b8337d7fa9c280f0fd7a4045947515a785637", size = 2428347, upload-time = "2025-12-18T15:46:39.41Z" }, - { url = "https://files.pythonhosted.org/packages/2c/08/f4b804a00c16e3ea994cb640a7c25c579b4f1fa674cde6a19fa0dfb0ae4f/pydantic-1.10.26-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3cfdd361addb6eb64ccd26ac356ad6514cee06a61ab26b27e16b5ed53108f77", size = 2212605, upload-time = "2025-12-18T15:46:41.006Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/0df4b9efef29bbc5e39f247fcba99060d15946b4463d82a5589cf7923d71/pydantic-1.10.26-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e4451951a9a93bf9a90576f3e25240b47ee49ab5236adccb8eff6ac943adf0f", size = 2753560, upload-time = "2025-12-18T15:46:43.215Z" }, - { url = "https://files.pythonhosted.org/packages/68/66/6ab6c1d3a116d05d2508fce64f96e35242938fac07544d611e11d0d363a0/pydantic-1.10.26-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9858ed44c6bea5f29ffe95308db9e62060791c877766c67dd5f55d072c8612b5", size = 2859235, upload-time = "2025-12-18T15:46:45.112Z" }, - { url = "https://files.pythonhosted.org/packages/61/4e/f1676bb0fcdf6ed2ce4670d7d1fc1d6c3a06d84497644acfbe02649503f1/pydantic-1.10.26-cp311-cp311-win_amd64.whl", hash = "sha256:ac1089f723e2106ebde434377d31239e00870a7563245072968e5af5cc4d33df", size = 2066646, upload-time = "2025-12-18T15:46:46.816Z" }, - { url = "https://files.pythonhosted.org/packages/02/6c/cd97a5a776c4515e6ee2ae81c2f2c5be51376dda6c31f965d7746ce0019f/pydantic-1.10.26-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:468d5b9cacfcaadc76ed0a4645354ab6f263ec01a63fb6d05630ea1df6ae453f", size = 2433795, upload-time = "2025-12-18T15:46:49.321Z" }, - { url = "https://files.pythonhosted.org/packages/47/12/de20affa30dcef728fcf9cc98e13ff4438c7a630de8d2f90eb38eba0891c/pydantic-1.10.26-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2c1b0b914be31671000ca25cf7ea17fcaaa68cfeadf6924529c5c5aa24b7ab1f", size = 2227387, upload-time = "2025-12-18T15:46:50.877Z" }, - { url = "https://files.pythonhosted.org/packages/7b/1d/9d65dcc5b8c17ba590f1f9f486e9306346831902318b7ee93f63516f4003/pydantic-1.10.26-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15b13b9f8ba8867095769e1156e0d7fbafa1f65b898dd40fd1c02e34430973cb", size = 2629594, upload-time = "2025-12-18T15:46:53.42Z" }, - { url = "https://files.pythonhosted.org/packages/3f/76/acb41409356789e23e1a7ef58f93821410c96409183ce314ddb58d97f23e/pydantic-1.10.26-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad7025ca324ae263d4313998e25078dcaec5f9ed0392c06dedb57e053cc8086b", size = 2745305, upload-time = "2025-12-18T15:46:55.987Z" }, - { url = "https://files.pythonhosted.org/packages/22/72/a98c0c5e527a66057d969fedd61675223c7975ade61acebbca9f1abd6dc0/pydantic-1.10.26-cp312-cp312-win_amd64.whl", hash = "sha256:4482b299874dabb88a6c3759e3d85c6557c407c3b586891f7d808d8a38b66b9c", size = 1937647, upload-time = "2025-12-18T15:46:57.905Z" }, - { url = "https://files.pythonhosted.org/packages/28/b9/17a5a5a421c23ac27486b977724a42c9d5f8b7f0f4aab054251066223900/pydantic-1.10.26-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1ae7913bb40a96c87e3d3f6fe4e918ef53bf181583de4e71824360a9b11aef1c", size = 2494599, upload-time = "2025-12-18T15:47:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8e/6e3bd4241076cf227b443d7577245dd5d181ecf40b3182fcb908bc8c197d/pydantic-1.10.26-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8154c13f58d4de5d3a856bb6c909c7370f41fb876a5952a503af6b975265f4ba", size = 2254391, upload-time = "2025-12-18T15:47:02.268Z" }, - { url = "https://files.pythonhosted.org/packages/a8/30/a1c4092eda2145ecbead6c92db489b223e101e1ba0da82576d0cf73dd422/pydantic-1.10.26-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f8af0507bf6118b054a9765fb2e402f18a8b70c964f420d95b525eb711122d62", size = 2609445, upload-time = "2025-12-18T15:47:04.909Z" }, - { url = "https://files.pythonhosted.org/packages/3a/2a/0491f1729ee4b7b6bc859ec22f69752f0c09bee1b66ac6f5f701136f34c3/pydantic-1.10.26-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dcb5a7318fb43189fde6af6f21ac7149c4bcbcfffc54bc87b5becddc46084847", size = 2732124, upload-time = "2025-12-18T15:47:07.464Z" }, - { url = "https://files.pythonhosted.org/packages/2a/56/b59f3b2f84e1df2b04ae768a1bb04d9f0288ff71b67cdcbb07683757b2c0/pydantic-1.10.26-cp313-cp313-win_amd64.whl", hash = "sha256:71cde228bc0600cf8619f0ee62db050d1880dcc477eba0e90b23011b4ee0f314", size = 1939888, upload-time = "2025-12-18T15:47:09.618Z" }, - { url = "https://files.pythonhosted.org/packages/d2/8b/0c3dc02d4b97790b0f199bf933f677c14e7be4a8d21307c5f2daae06aa41/pydantic-1.10.26-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6b40730cc81d53d515dc0b8bb5c9b43fadb9bed46de4a3c03bd95e8571616dba", size = 2502689, upload-time = "2025-12-18T15:47:12.308Z" }, - { url = "https://files.pythonhosted.org/packages/d4/9d/d31aeea45542b2ae4b09ecba92b88aaba696b801c31919811aa979a1242d/pydantic-1.10.26-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c3bbb9c0eecdf599e4db9b372fa9cc55be12e80a0d9c6d307950a39050cb0e37", size = 2269494, upload-time = "2025-12-18T15:47:14.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/c1/3a4d069593283ca4dd0006039ba33644e21e432cddc09da706ac50441610/pydantic-1.10.26-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc2e3fe7bc4993626ef6b6fa855defafa1d6f8996aa1caef2deb83c5ac4d043a", size = 2620047, upload-time = "2025-12-18T15:47:17.089Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0e/340c3d29197d99c15ab04093d43bb9c9d0fd17c2a34b80cb9d36ed732b09/pydantic-1.10.26-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:36d9e46b588aaeb1dcd2409fa4c467fe0b331f3cc9f227b03a7a00643704e962", size = 2747625, upload-time = "2025-12-18T15:47:19.21Z" }, - { url = "https://files.pythonhosted.org/packages/1e/58/f12ab3727339b172c830b32151919456b67787cdfe8808b2568b322fb15c/pydantic-1.10.26-cp314-cp314-win_amd64.whl", hash = "sha256:81ce3c8616d12a7be31b4aadfd3434f78f6b44b75adbfaec2fe1ad4f7f999b8c", size = 1976436, upload-time = "2025-12-18T15:47:21.384Z" }, - { url = "https://files.pythonhosted.org/packages/1f/98/556e82f00b98486def0b8af85da95e69d2be7e367cf2431408e108bc3095/pydantic-1.10.26-py3-none-any.whl", hash = "sha256:c43ad70dc3ce7787543d563792426a16fd7895e14be4b194b5665e36459dd917", size = 166975, upload-time = "2025-12-18T15:47:44.927Z" }, -] - -[[package]] -name = "pydeck" -version = "0.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240, upload-time = "2024-05-10T15:36:21.153Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403, upload-time = "2024-05-10T15:36:17.36Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pymdown-extensions" -version = "10.21.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/08/f1c908c581fd11913da4711ea7ba32c0eee40b0190000996bb863b0c9349/pymdown_extensions-10.21.2.tar.gz", hash = "sha256:c3f55a5b8a1d0edf6699e35dcbea71d978d34ff3fa79f3d807b8a5b3fa90fbdc", size = 853922, upload-time = "2026-03-29T15:01:55.233Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl", hash = "sha256:5c0fd2a2bea14eb39af8ff284f1066d898ab2187d81b889b75d46d4348c01638", size = 268901, upload-time = "2026-03-29T15:01:53.244Z" }, -] - -[[package]] -name = "pymilvus" -version = "2.5.18" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "grpcio" }, - { name = "milvus-lite", marker = "sys_platform != 'win32'" }, - { name = "pandas" }, - { name = "protobuf" }, - { name = "python-dotenv" }, - { name = "setuptools" }, - { name = "ujson" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d3/13/899185f025802ba80255faa8e45b3f3bf9cb7bab2d4235e12e3322c8e2a4/pymilvus-2.5.18.tar.gz", hash = "sha256:9e517076068e98dac51c018bc0dfe1f651d936154e2e2d9ad6c7b3dab1164e2d", size = 1285482, upload-time = "2025-12-02T10:58:25.399Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/9c/a2b50b2b369814003460ca12a3c195fbf11b89bc1a861c2aa737c33ad7f9/pymilvus-2.5.18-py3-none-any.whl", hash = "sha256:1b78badcfa8d62db7d0b29193fc0422e4676873ff1c745a9d75c2c885d7a7e32", size = 244089, upload-time = "2025-12-02T10:58:23.944Z" }, -] - -[[package]] -name = "pymongo" -version = "4.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dnspython" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/65/9c/a4895c4b785fc9865a84a56e14b5bd21ca75aadc3dab79c14187cdca189b/pymongo-4.16.0.tar.gz", hash = "sha256:8ba8405065f6e258a6f872fe62d797a28f383a12178c7153c01ed04e845c600c", size = 2495323, upload-time = "2026-01-07T18:05:48.107Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/3a/907414a763c4270b581ad6d960d0c6221b74a70eda216a1fdd8fa82ba89f/pymongo-4.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6f2077ec24e2f1248f9cac7b9a2dfb894e50cc7939fcebfb1759f99304caabef", size = 862561, upload-time = "2026-01-07T18:04:00.628Z" }, - { url = "https://files.pythonhosted.org/packages/8c/58/787d8225dd65cb2383c447346ea5e200ecfde89962d531111521e3b53018/pymongo-4.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d4f7ba040f72a9f43a44059872af5a8c8c660aa5d7f90d5344f2ed1c3c02721", size = 862923, upload-time = "2026-01-07T18:04:02.213Z" }, - { url = "https://files.pythonhosted.org/packages/5d/a7/cc2865aae32bc77ade7b35f957a58df52680d7f8506f93c6edbf458e5738/pymongo-4.16.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8a0f73af1ea56c422b2dcfc0437459148a799ef4231c6aee189d2d4c59d6728f", size = 1426779, upload-time = "2026-01-07T18:04:03.942Z" }, - { url = "https://files.pythonhosted.org/packages/81/25/3e96eb7998eec05382174da2fefc58d28613f46bbdf821045539d0ed60ab/pymongo-4.16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa30cd16ddd2f216d07ba01d9635c873e97ddb041c61cf0847254edc37d1c60e", size = 1454207, upload-time = "2026-01-07T18:04:05.387Z" }, - { url = "https://files.pythonhosted.org/packages/86/7b/8e817a7df8c5d565d39dd4ca417a5e0ef46cc5cc19aea9405f403fec6449/pymongo-4.16.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d638b0b1b294d95d0fdc73688a3b61e05cc4188872818cd240d51460ccabcb5", size = 1511654, upload-time = "2026-01-07T18:04:08.458Z" }, - { url = "https://files.pythonhosted.org/packages/39/7a/50c4d075ccefcd281cdcfccc5494caa5665b096b85e65a5d6afabb80e09e/pymongo-4.16.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:21d02cc10a158daa20cb040985e280e7e439832fc6b7857bff3d53ef6914ad50", size = 1496794, upload-time = "2026-01-07T18:04:10.355Z" }, - { url = "https://files.pythonhosted.org/packages/0f/cd/ebdc1aaca5deeaf47310c369ef4083e8550e04e7bf7e3752cfb7d95fcdb8/pymongo-4.16.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fbb8d3552c2ad99d9e236003c0b5f96d5f05e29386ba7abae73949bfebc13dd", size = 1448371, upload-time = "2026-01-07T18:04:11.76Z" }, - { url = "https://files.pythonhosted.org/packages/3d/c9/50fdd78c37f68ea49d590c027c96919fbccfd98f3a4cb39f84f79970bd37/pymongo-4.16.0-cp311-cp311-win32.whl", hash = "sha256:be1099a8295b1a722d03fb7b48be895d30f4301419a583dcf50e9045968a041c", size = 841024, upload-time = "2026-01-07T18:04:13.522Z" }, - { url = "https://files.pythonhosted.org/packages/4a/dd/a3aa1ade0cf9980744db703570afac70a62c85b432c391dea0577f6da7bb/pymongo-4.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:61567f712bda04c7545a037e3284b4367cad8d29b3dec84b4bf3b2147020a75b", size = 855838, upload-time = "2026-01-07T18:04:14.923Z" }, - { url = "https://files.pythonhosted.org/packages/bf/10/9ad82593ccb895e8722e4884bad4c5ce5e8ff6683b740d7823a6c2bcfacf/pymongo-4.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:c53338613043038005bf2e41a2fafa08d29cdbc0ce80891b5366c819456c1ae9", size = 845007, upload-time = "2026-01-07T18:04:17.099Z" }, - { url = "https://files.pythonhosted.org/packages/6a/03/6dd7c53cbde98de469a3e6fb893af896dca644c476beb0f0c6342bcc368b/pymongo-4.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bd4911c40a43a821dfd93038ac824b756b6e703e26e951718522d29f6eb166a8", size = 917619, upload-time = "2026-01-07T18:04:19.173Z" }, - { url = "https://files.pythonhosted.org/packages/73/e1/328915f2734ea1f355dc9b0e98505ff670f5fab8be5e951d6ed70971c6aa/pymongo-4.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25a6b03a68f9907ea6ec8bc7cf4c58a1b51a18e23394f962a6402f8e46d41211", size = 917364, upload-time = "2026-01-07T18:04:20.861Z" }, - { url = "https://files.pythonhosted.org/packages/41/fe/4769874dd9812a1bc2880a9785e61eba5340da966af888dd430392790ae0/pymongo-4.16.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:91ac0cb0fe2bf17616c2039dac88d7c9a5088f5cb5829b27c9d250e053664d31", size = 1686901, upload-time = "2026-01-07T18:04:22.219Z" }, - { url = "https://files.pythonhosted.org/packages/fa/8d/15707b9669fdc517bbc552ac60da7124dafe7ac1552819b51e97ed4038b4/pymongo-4.16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf0ec79e8ca7077f455d14d915d629385153b6a11abc0b93283ed73a8013e376", size = 1723034, upload-time = "2026-01-07T18:04:24.055Z" }, - { url = "https://files.pythonhosted.org/packages/5b/af/3d5d16ff11d447d40c1472da1b366a31c7380d7ea2922a449c7f7f495567/pymongo-4.16.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2d0082631a7510318befc2b4fdab140481eb4b9dd62d9245e042157085da2a70", size = 1797161, upload-time = "2026-01-07T18:04:25.964Z" }, - { url = "https://files.pythonhosted.org/packages/fb/04/725ab8664eeec73ec125b5a873448d80f5d8cf2750aaaf804cbc538a50a5/pymongo-4.16.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85dc2f3444c346ea019a371e321ac868a4fab513b7a55fe368f0cc78de8177cc", size = 1780938, upload-time = "2026-01-07T18:04:28.745Z" }, - { url = "https://files.pythonhosted.org/packages/22/50/dd7e9095e1ca35f93c3c844c92eb6eb0bc491caeb2c9bff3b32fe3c9b18f/pymongo-4.16.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dabbf3c14de75a20cc3c30bf0c6527157224a93dfb605838eabb1a2ee3be008d", size = 1714342, upload-time = "2026-01-07T18:04:30.331Z" }, - { url = "https://files.pythonhosted.org/packages/03/c9/542776987d5c31ae8e93e92680ea2b6e5a2295f398b25756234cabf38a39/pymongo-4.16.0-cp312-cp312-win32.whl", hash = "sha256:60307bb91e0ab44e560fe3a211087748b2b5f3e31f403baf41f5b7b0a70bd104", size = 887868, upload-time = "2026-01-07T18:04:32.124Z" }, - { url = "https://files.pythonhosted.org/packages/2e/d4/b4045a7ccc5680fb496d01edf749c7a9367cc8762fbdf7516cf807ef679b/pymongo-4.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:f513b2c6c0d5c491f478422f6b5b5c27ac1af06a54c93ef8631806f7231bd92e", size = 907554, upload-time = "2026-01-07T18:04:33.685Z" }, - { url = "https://files.pythonhosted.org/packages/60/4c/33f75713d50d5247f2258405142c0318ff32c6f8976171c4fcae87a9dbdf/pymongo-4.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:dfc320f08ea9a7ec5b2403dc4e8150636f0d6150f4b9792faaae539c88e7db3b", size = 892971, upload-time = "2026-01-07T18:04:35.594Z" }, - { url = "https://files.pythonhosted.org/packages/47/84/148d8b5da8260f4679d6665196ae04ab14ffdf06f5fe670b0ab11942951f/pymongo-4.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d15f060bc6d0964a8bb70aba8f0cb6d11ae99715438f640cff11bbcf172eb0e8", size = 972009, upload-time = "2026-01-07T18:04:38.303Z" }, - { url = "https://files.pythonhosted.org/packages/1e/5e/9f3a8daf583d0adaaa033a3e3e58194d2282737dc164014ff33c7a081103/pymongo-4.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a19ea46a0fe71248965305a020bc076a163311aefbaa1d83e47d06fa30ac747", size = 971784, upload-time = "2026-01-07T18:04:39.669Z" }, - { url = "https://files.pythonhosted.org/packages/ad/f2/b6c24361fcde24946198573c0176406bfd5f7b8538335f3d939487055322/pymongo-4.16.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:311d4549d6bf1f8c61d025965aebb5ba29d1481dc6471693ab91610aaffbc0eb", size = 1947174, upload-time = "2026-01-07T18:04:41.368Z" }, - { url = "https://files.pythonhosted.org/packages/47/1a/8634192f98cf740b3d174e1018dd0350018607d5bd8ac35a666dc49c732b/pymongo-4.16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46ffb728d92dd5b09fc034ed91acf5595657c7ca17d4cf3751322cd554153c17", size = 1991727, upload-time = "2026-01-07T18:04:42.965Z" }, - { url = "https://files.pythonhosted.org/packages/5a/2f/0c47ac84572b28e23028a23a3798a1f725e1c23b0cf1c1424678d16aff42/pymongo-4.16.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:acda193f440dd88c2023cb00aa8bd7b93a9df59978306d14d87a8b12fe426b05", size = 2082497, upload-time = "2026-01-07T18:04:44.652Z" }, - { url = "https://files.pythonhosted.org/packages/ba/57/9f46ef9c862b2f0cf5ce798f3541c201c574128d31ded407ba4b3918d7b6/pymongo-4.16.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d9fdb386cf958e6ef6ff537d6149be7edb76c3268cd6833e6c36aa447e4443f", size = 2064947, upload-time = "2026-01-07T18:04:46.228Z" }, - { url = "https://files.pythonhosted.org/packages/b8/56/5421c0998f38e32288100a07f6cb2f5f9f352522157c901910cb2927e211/pymongo-4.16.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91899dd7fb9a8c50f09c3c1cf0cb73bfbe2737f511f641f19b9650deb61c00ca", size = 1980478, upload-time = "2026-01-07T18:04:48.017Z" }, - { url = "https://files.pythonhosted.org/packages/92/93/bfc448d025e12313a937d6e1e0101b50cc9751636b4b170e600fe3203063/pymongo-4.16.0-cp313-cp313-win32.whl", hash = "sha256:2cd60cd1e05de7f01927f8e25ca26b3ea2c09de8723241e5d3bcfdc70eaff76b", size = 934672, upload-time = "2026-01-07T18:04:49.538Z" }, - { url = "https://files.pythonhosted.org/packages/96/10/12710a5e01218d50c3dd165fd72c5ed2699285f77348a3b1a119a191d826/pymongo-4.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3ead8a0050c53eaa55935895d6919d393d0328ec24b2b9115bdbe881aa222673", size = 959237, upload-time = "2026-01-07T18:04:51.382Z" }, - { url = "https://files.pythonhosted.org/packages/0c/56/d288bcd1d05bc17ec69df1d0b1d67bc710c7c5dbef86033a5a4d2e2b08e6/pymongo-4.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:dbbc5b254c36c37d10abb50e899bc3939bbb7ab1e7c659614409af99bd3e7675", size = 940909, upload-time = "2026-01-07T18:04:52.904Z" }, - { url = "https://files.pythonhosted.org/packages/30/9e/4d343f8d0512002fce17915a89477b9f916bda1205729e042d8f23acf194/pymongo-4.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:8a254d49a9ffe9d7f888e3c677eed3729b14ce85abb08cd74732cead6ccc3c66", size = 1026634, upload-time = "2026-01-07T18:04:54.359Z" }, - { url = "https://files.pythonhosted.org/packages/c3/e3/341f88c5535df40c0450fda915f582757bb7d988cdfc92990a5e27c4c324/pymongo-4.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a1bf44e13cf2d44d2ea2e928a8140d5d667304abe1a61c4d55b4906f389fbe64", size = 1026252, upload-time = "2026-01-07T18:04:56.642Z" }, - { url = "https://files.pythonhosted.org/packages/af/64/9471b22eb98f0a2ca0b8e09393de048502111b2b5b14ab1bd9e39708aab5/pymongo-4.16.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f1c5f1f818b669875d191323a48912d3fcd2e4906410e8297bb09ac50c4d5ccc", size = 2207399, upload-time = "2026-01-07T18:04:58.255Z" }, - { url = "https://files.pythonhosted.org/packages/87/ac/47c4d50b25a02f21764f140295a2efaa583ee7f17992a5e5fa542b3a690f/pymongo-4.16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77cfd37a43a53b02b7bd930457c7994c924ad8bbe8dff91817904bcbf291b371", size = 2260595, upload-time = "2026-01-07T18:04:59.788Z" }, - { url = "https://files.pythonhosted.org/packages/ee/1b/0ce1ce9dd036417646b2fe6f63b58127acff3cf96eeb630c34ec9cd675ff/pymongo-4.16.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:36ef2fee50eee669587d742fb456e349634b4fcf8926208766078b089054b24b", size = 2366958, upload-time = "2026-01-07T18:05:01.942Z" }, - { url = "https://files.pythonhosted.org/packages/3e/3c/a5a17c0d413aa9d6c17bc35c2b472e9e79cda8068ba8e93433b5f43028e9/pymongo-4.16.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55f8d5a6fe2fa0b823674db2293f92d74cd5f970bc0360f409a1fc21003862d3", size = 2346081, upload-time = "2026-01-07T18:05:03.576Z" }, - { url = "https://files.pythonhosted.org/packages/65/19/f815533d1a88fb8a3b6c6e895bb085ffdae68ccb1e6ed7102202a307f8e2/pymongo-4.16.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9caacac0dd105e2555521002e2d17afc08665187017b466b5753e84c016628e6", size = 2246053, upload-time = "2026-01-07T18:05:05.459Z" }, - { url = "https://files.pythonhosted.org/packages/c6/88/4be3ec78828dc64b212c123114bd6ae8db5b7676085a7b43cc75d0131bd2/pymongo-4.16.0-cp314-cp314-win32.whl", hash = "sha256:c789236366525c3ee3cd6e4e450a9ff629a7d1f4d88b8e18a0aea0615fd7ecf8", size = 989461, upload-time = "2026-01-07T18:05:07.018Z" }, - { url = "https://files.pythonhosted.org/packages/af/5a/ab8d5af76421b34db483c9c8ebc3a2199fb80ae63dc7e18f4cf1df46306a/pymongo-4.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b0714d7764efb29bf9d3c51c964aed7c4c7237b341f9346f15ceaf8321fdb35", size = 1017803, upload-time = "2026-01-07T18:05:08.499Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f4/98d68020728ac6423cf02d17cfd8226bf6cce5690b163d30d3f705e8297e/pymongo-4.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:12762e7cc0f8374a8cae3b9f9ed8dabb5d438c7b33329232dd9b7de783454033", size = 997184, upload-time = "2026-01-07T18:05:09.944Z" }, - { url = "https://files.pythonhosted.org/packages/50/00/dc3a271daf06401825b9c1f4f76f018182c7738281ea54b9762aea0560c1/pymongo-4.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1c01e8a7cd0ea66baf64a118005535ab5bf9f9eb63a1b50ac3935dccf9a54abe", size = 1083303, upload-time = "2026-01-07T18:05:11.702Z" }, - { url = "https://files.pythonhosted.org/packages/b8/4b/b5375ee21d12eababe46215011ebc63801c0d2c5ffdf203849d0d79f9852/pymongo-4.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4c4872299ebe315a79f7f922051061634a64fda95b6b17677ba57ef00b2ba2a4", size = 1083233, upload-time = "2026-01-07T18:05:13.182Z" }, - { url = "https://files.pythonhosted.org/packages/ee/e3/52efa3ca900622c7dcb56c5e70f15c906816d98905c22d2ee1f84d9a7b60/pymongo-4.16.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:78037d02389745e247fe5ab0bcad5d1ab30726eaac3ad79219c7d6bbb07eec53", size = 2527438, upload-time = "2026-01-07T18:05:14.981Z" }, - { url = "https://files.pythonhosted.org/packages/cb/96/43b1be151c734e7766c725444bcbfa1de6b60cc66bfb406203746839dd25/pymongo-4.16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c126fb72be2518395cc0465d4bae03125119136462e1945aea19840e45d89cfc", size = 2600399, upload-time = "2026-01-07T18:05:16.794Z" }, - { url = "https://files.pythonhosted.org/packages/e7/62/fa64a5045dfe3a1cd9217232c848256e7bc0136cffb7da4735c5e0d30e40/pymongo-4.16.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f3867dc225d9423c245a51eaac2cfcd53dde8e0a8d8090bb6aed6e31bd6c2d4f", size = 2720960, upload-time = "2026-01-07T18:05:18.498Z" }, - { url = "https://files.pythonhosted.org/packages/54/7b/01577eb97e605502821273a5bc16ce0fb0be5c978fe03acdbff471471202/pymongo-4.16.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f25001a955073b80510c0c3db0e043dbbc36904fd69e511c74e3d8640b8a5111", size = 2699344, upload-time = "2026-01-07T18:05:20.073Z" }, - { url = "https://files.pythonhosted.org/packages/55/68/6ef6372d516f703479c3b6cbbc45a5afd307173b1cbaccd724e23919bb1a/pymongo-4.16.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d9885aad05f82fd7ea0c9ca505d60939746b39263fa273d0125170da8f59098", size = 2577133, upload-time = "2026-01-07T18:05:22.052Z" }, - { url = "https://files.pythonhosted.org/packages/15/c7/b5337093bb01da852f945802328665f85f8109dbe91d81ea2afe5ff059b9/pymongo-4.16.0-cp314-cp314t-win32.whl", hash = "sha256:948152b30eddeae8355495f9943a3bf66b708295c0b9b6f467de1c620f215487", size = 1040560, upload-time = "2026-01-07T18:05:23.888Z" }, - { url = "https://files.pythonhosted.org/packages/96/8c/5b448cd1b103f3889d5713dda37304c81020ff88e38a826e8a75ddff4610/pymongo-4.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f6e42c1bc985d9beee884780ae6048790eb4cd565c46251932906bdb1630034a", size = 1075081, upload-time = "2026-01-07T18:05:26.874Z" }, - { url = "https://files.pythonhosted.org/packages/32/cd/ddc794cdc8500f6f28c119c624252fb6dfb19481c6d7ed150f13cf468a6d/pymongo-4.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6b2a20edb5452ac8daa395890eeb076c570790dfce6b7a44d788af74c2f8cf96", size = 1047725, upload-time = "2026-01-07T18:05:28.47Z" }, -] - -[[package]] -name = "pymysql" -version = "1.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/ae/1fe3fcd9f959efa0ebe200b8de88b5a5ce3e767e38c7ac32fb179f16a388/pymysql-1.1.2.tar.gz", hash = "sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03", size = 48258, upload-time = "2025-08-24T12:55:55.146Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/4c/ad33b92b9864cbde84f259d5df035a6447f91891f5be77788e2a3892bce3/pymysql-1.1.2-py3-none-any.whl", hash = "sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9", size = 45300, upload-time = "2025-08-24T12:55:53.394Z" }, -] - -[[package]] -name = "pyparsing" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, -] - -[[package]] -name = "pypika" -version = "0.51.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/78/cbaebba88e05e2dcda13ca203131b38d3640219f20ebb49676d26714861b/pypika-0.51.1.tar.gz", hash = "sha256:c30c7c1048fbf056fd3920c5a2b88b0c29dd190a9b2bee971fd17e4abe4d0ebe", size = 80919, upload-time = "2026-02-04T11:27:48.304Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/83/c77dfeed04022e8930b08eedca2b6e5efed256ab3321396fde90066efb65/pypika-0.51.1-py2.py3-none-any.whl", hash = "sha256:77985b4d7ce71b9905255bf12468cf598349e98837c037541cfc240e528aec46", size = 60585, upload-time = "2026-02-04T11:27:46.251Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, -] - -[[package]] -name = "pytokens" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" }, - { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" }, - { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" }, - { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" }, - { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" }, - { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, - { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, - { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, - { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, - { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, - { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, - { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, - { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, - { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, - { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, - { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, - { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, - { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, - { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, - { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, - { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, - { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, - { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, - { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, - { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, -] - -[[package]] -name = "pytz" -version = "2026.1.post1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" }, -] - -[[package]] -name = "pyvespa" -version = "1.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "certifi" }, - { name = "cryptography" }, - { name = "docker" }, - { name = "fastcore" }, - { name = "httpr" }, - { name = "httpx", extra = ["http2"] }, - { name = "jinja2" }, - { name = "lxml" }, - { name = "python-dateutil" }, - { name = "requests" }, - { name = "requests-toolbelt" }, - { name = "tenacity" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d5/aa/733a6e04652e34cb4c7184b818023d4a7e73a79a78074baa76a84b1290b3/pyvespa-1.1.2.tar.gz", hash = "sha256:da7d591db22d3e3021956a29bf3b4fab33e15cf6ccd71cc8c117d8b3b178781e", size = 153030, upload-time = "2026-03-04T09:08:59.504Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/02/9904b061aee06e1ac8e22f6ff619cc1c7c05310689a172c8e89abfddfc9c/pyvespa-1.1.2-py3-none-any.whl", hash = "sha256:3197e7c69c0bde53b663542f6b0a0d7c00420ef3cbbaa90d6af83cef44952ec4", size = 163077, upload-time = "2026-03-04T09:08:57.677Z" }, -] - -[[package]] -name = "pywin32" -version = "311" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, - { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, - { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, - { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, - { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, - { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, - { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, - { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, - { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, - { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, - { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, - { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, - { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "qdrant-client" -version = "1.17.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "grpcio" }, - { name = "httpx", extra = ["http2"] }, - { name = "numpy" }, - { name = "portalocker" }, - { name = "protobuf" }, - { name = "pydantic" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/30/dd/f8a8261b83946af3cd65943c93c4f83e044f01184e8525404989d22a81a5/qdrant_client-1.17.1.tar.gz", hash = "sha256:22f990bbd63485ed97ba551a4c498181fcb723f71dcab5d6e4e43fe1050a2bc0", size = 344979, upload-time = "2026-03-13T17:13:44.678Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/68/69/77d1a971c4b933e8c79403e99bcbb790463da5e48333cc4fd5d412c63c98/qdrant_client-1.17.1-py3-none-any.whl", hash = "sha256:6cda4064adfeaf211c751f3fbc00edbbdb499850918c7aff4855a9a759d56cbd", size = 389947, upload-time = "2026-03-13T17:13:43.156Z" }, -] - -[[package]] -name = "redis" -version = "7.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "async-timeout", marker = "python_full_version < '3.11.3'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/7f/3759b1d0d72b7c92f0d70ffd9dc962b7b7b5ee74e135f9d7d8ab06b8a318/redis-7.4.0.tar.gz", hash = "sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad", size = 4943913, upload-time = "2026-03-24T09:14:37.53Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/3a/95deec7db1eb53979973ebd156f3369a72732208d1391cd2e5d127062a32/redis-7.4.0-py3-none-any.whl", hash = "sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec", size = 409772, upload-time = "2026-03-24T09:14:35.968Z" }, -] - -[[package]] -name = "referencing" -version = "0.37.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, -] - -[[package]] -name = "requests" -version = "2.33.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, -] - -[[package]] -name = "requests-toolbelt" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, -] - -[[package]] -name = "rich" -version = "14.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, - { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, - { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, - { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, - { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, - { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, - { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, - { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, - { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, - { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, - { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, - { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, - { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, - { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, - { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, - { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, - { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, - { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, - { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, - { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, - { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, - { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, - { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, - { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, - { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, - { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, -] - -[[package]] -name = "ruff" -version = "0.15.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/d9/aa3f7d59a10ef6b14fe3431706f854dbf03c5976be614a9796d36326810c/ruff-0.15.10.tar.gz", hash = "sha256:d1f86e67ebfdef88e00faefa1552b5e510e1d35f3be7d423dc7e84e63788c94e", size = 4631728, upload-time = "2026-04-09T14:06:09.884Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/00/a1c2fdc9939b2c03691edbda290afcd297f1f389196172826b03d6b6a595/ruff-0.15.10-py3-none-linux_armv6l.whl", hash = "sha256:0744e31482f8f7d0d10a11fcbf897af272fefdfcb10f5af907b18c2813ff4d5f", size = 10563362, upload-time = "2026-04-09T14:06:21.189Z" }, - { url = "https://files.pythonhosted.org/packages/5c/15/006990029aea0bebe9d33c73c3e28c80c391ebdba408d1b08496f00d422d/ruff-0.15.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b1e7c16ea0ff5a53b7c2df52d947e685973049be1cdfe2b59a9c43601897b22e", size = 10951122, upload-time = "2026-04-09T14:06:02.236Z" }, - { url = "https://files.pythonhosted.org/packages/f2/c0/4ac978fe874d0618c7da647862afe697b281c2806f13ce904ad652fa87e4/ruff-0.15.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:93cc06a19e5155b4441dd72808fdf84290d84ad8a39ca3b0f994363ade4cebb1", size = 10314005, upload-time = "2026-04-09T14:06:00.026Z" }, - { url = "https://files.pythonhosted.org/packages/da/73/c209138a5c98c0d321266372fc4e33ad43d506d7e5dd817dd89b60a8548f/ruff-0.15.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83e1dd04312997c99ea6965df66a14fb4f03ba978564574ffc68b0d61fd3989e", size = 10643450, upload-time = "2026-04-09T14:05:42.137Z" }, - { url = "https://files.pythonhosted.org/packages/ec/76/0deec355d8ec10709653635b1f90856735302cb8e149acfdf6f82a5feb70/ruff-0.15.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8154d43684e4333360fedd11aaa40b1b08a4e37d8ffa9d95fee6fa5b37b6fab1", size = 10379597, upload-time = "2026-04-09T14:05:49.984Z" }, - { url = "https://files.pythonhosted.org/packages/dc/be/86bba8fc8798c081e28a4b3bb6d143ccad3fd5f6f024f02002b8f08a9fa3/ruff-0.15.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ab88715f3a6deb6bde6c227f3a123410bec7b855c3ae331b4c006189e895cef", size = 11146645, upload-time = "2026-04-09T14:06:12.246Z" }, - { url = "https://files.pythonhosted.org/packages/a8/89/140025e65911b281c57be1d385ba1d932c2366ca88ae6663685aed8d4881/ruff-0.15.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a768ff5969b4f44c349d48edf4ab4f91eddb27fd9d77799598e130fb628aa158", size = 12030289, upload-time = "2026-04-09T14:06:04.776Z" }, - { url = "https://files.pythonhosted.org/packages/88/de/ddacca9545a5e01332567db01d44bd8cf725f2db3b3d61a80550b48308ea/ruff-0.15.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ee3ef42dab7078bda5ff6a1bcba8539e9857deb447132ad5566a038674540d0", size = 11496266, upload-time = "2026-04-09T14:05:55.485Z" }, - { url = "https://files.pythonhosted.org/packages/bc/bb/7ddb00a83760ff4a83c4e2fc231fd63937cc7317c10c82f583302e0f6586/ruff-0.15.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51cb8cc943e891ba99989dd92d61e29b1d231e14811db9be6440ecf25d5c1609", size = 11256418, upload-time = "2026-04-09T14:05:57.69Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8d/55de0d35aacf6cd50b6ee91ee0f291672080021896543776f4170fc5c454/ruff-0.15.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:e59c9bdc056a320fb9ea1700a8d591718b8faf78af065484e801258d3a76bc3f", size = 11288416, upload-time = "2026-04-09T14:05:44.695Z" }, - { url = "https://files.pythonhosted.org/packages/68/cf/9438b1a27426ec46a80e0a718093c7f958ef72f43eb3111862949ead3cc1/ruff-0.15.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:136c00ca2f47b0018b073f28cb5c1506642a830ea941a60354b0e8bc8076b151", size = 10621053, upload-time = "2026-04-09T14:05:52.782Z" }, - { url = "https://files.pythonhosted.org/packages/4c/50/e29be6e2c135e9cd4cb15fbade49d6a2717e009dff3766dd080fcb82e251/ruff-0.15.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b80a2f3c9c8a950d6237f2ca12b206bccff626139be9fa005f14feb881a1ae8", size = 10378302, upload-time = "2026-04-09T14:06:14.361Z" }, - { url = "https://files.pythonhosted.org/packages/18/2f/e0b36a6f99c51bb89f3a30239bc7bf97e87a37ae80aa2d6542d6e5150364/ruff-0.15.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e3e53c588164dc025b671c9df2462429d60357ea91af7e92e9d56c565a9f1b07", size = 10850074, upload-time = "2026-04-09T14:06:16.581Z" }, - { url = "https://files.pythonhosted.org/packages/11/08/874da392558ce087a0f9b709dc6ec0d60cbc694c1c772dab8d5f31efe8cb/ruff-0.15.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b0c52744cf9f143a393e284125d2576140b68264a93c6716464e129a3e9adb48", size = 11358051, upload-time = "2026-04-09T14:06:18.948Z" }, - { url = "https://files.pythonhosted.org/packages/e4/46/602938f030adfa043e67112b73821024dc79f3ab4df5474c25fa4c1d2d14/ruff-0.15.10-py3-none-win32.whl", hash = "sha256:d4272e87e801e9a27a2e8df7b21011c909d9ddd82f4f3281d269b6ba19789ca5", size = 10588964, upload-time = "2026-04-09T14:06:07.14Z" }, - { url = "https://files.pythonhosted.org/packages/25/b6/261225b875d7a13b33a6d02508c39c28450b2041bb01d0f7f1a83d569512/ruff-0.15.10-py3-none-win_amd64.whl", hash = "sha256:28cb32d53203242d403d819fd6983152489b12e4a3ae44993543d6fe62ab42ed", size = 11745044, upload-time = "2026-04-09T14:05:39.473Z" }, - { url = "https://files.pythonhosted.org/packages/58/ed/dea90a65b7d9e69888890fb14c90d7f51bf0c1e82ad800aeb0160e4bacfd/ruff-0.15.10-py3-none-win_arm64.whl", hash = "sha256:601d1610a9e1f1c2165a4f561eeaa2e2ea1e97f3287c5aa258d3dab8b57c6188", size = 11035607, upload-time = "2026-04-09T14:05:47.593Z" }, -] - -[[package]] -name = "s3fs" -version = "2026.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiobotocore" }, - { name = "aiohttp" }, - { name = "fsspec" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0b/93/093972862fb9c2fdc24ecf8d6d2212853df1945eddf26ba2625e8eaeee66/s3fs-2026.3.0.tar.gz", hash = "sha256:ce8b30a9dc5e01c5127c96cb7377290243a689a251ef9257336ac29d72d7b0d8", size = 85986, upload-time = "2026-03-27T19:28:20.963Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/52/5ccdc01f7a8a61357d15a66b5d8a6580aa8529cb33f32e6cbb71c52622c5/s3fs-2026.3.0-py3-none-any.whl", hash = "sha256:2fa40a64c03003cfa5ae0e352788d97aa78ae8f9e25ea98b28ce9d21ba10c1b8", size = 32399, upload-time = "2026-03-27T19:28:19.702Z" }, -] - -[[package]] -name = "scikit-learn" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "joblib" }, - { name = "numpy" }, - { name = "scipy" }, - { name = "threadpoolctl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/92/53ea2181da8ac6bf27170191028aee7251f8f841f8d3edbfdcaf2008fde9/scikit_learn-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da", size = 8595835, upload-time = "2025-12-10T07:07:39.385Z" }, - { url = "https://files.pythonhosted.org/packages/01/18/d154dc1638803adf987910cdd07097d9c526663a55666a97c124d09fb96a/scikit_learn-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1", size = 8080381, upload-time = "2025-12-10T07:07:41.93Z" }, - { url = "https://files.pythonhosted.org/packages/8a/44/226142fcb7b7101e64fdee5f49dbe6288d4c7af8abf593237b70fca080a4/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b", size = 8799632, upload-time = "2025-12-10T07:07:43.899Z" }, - { url = "https://files.pythonhosted.org/packages/36/4d/4a67f30778a45d542bbea5db2dbfa1e9e100bf9ba64aefe34215ba9f11f6/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1", size = 9103788, upload-time = "2025-12-10T07:07:45.982Z" }, - { url = "https://files.pythonhosted.org/packages/89/3c/45c352094cfa60050bcbb967b1faf246b22e93cb459f2f907b600f2ceda5/scikit_learn-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b", size = 8081706, upload-time = "2025-12-10T07:07:48.111Z" }, - { url = "https://files.pythonhosted.org/packages/3d/46/5416595bb395757f754feb20c3d776553a386b661658fb21b7c814e89efe/scikit_learn-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961", size = 7688451, upload-time = "2025-12-10T07:07:49.873Z" }, - { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242, upload-time = "2025-12-10T07:07:51.568Z" }, - { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075, upload-time = "2025-12-10T07:07:53.697Z" }, - { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492, upload-time = "2025-12-10T07:07:55.574Z" }, - { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904, upload-time = "2025-12-10T07:07:57.666Z" }, - { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359, upload-time = "2025-12-10T07:07:59.838Z" }, - { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898, upload-time = "2025-12-10T07:08:01.36Z" }, - { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770, upload-time = "2025-12-10T07:08:03.251Z" }, - { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458, upload-time = "2025-12-10T07:08:05.336Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341, upload-time = "2025-12-10T07:08:07.732Z" }, - { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022, upload-time = "2025-12-10T07:08:09.862Z" }, - { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409, upload-time = "2025-12-10T07:08:12.028Z" }, - { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760, upload-time = "2025-12-10T07:08:13.688Z" }, - { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045, upload-time = "2025-12-10T07:08:15.215Z" }, - { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324, upload-time = "2025-12-10T07:08:17.561Z" }, - { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651, upload-time = "2025-12-10T07:08:19.952Z" }, - { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045, upload-time = "2025-12-10T07:08:22.11Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994, upload-time = "2025-12-10T07:08:23.943Z" }, - { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518, upload-time = "2025-12-10T07:08:25.71Z" }, - { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667, upload-time = "2025-12-10T07:08:27.541Z" }, - { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524, upload-time = "2025-12-10T07:08:29.822Z" }, - { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133, upload-time = "2025-12-10T07:08:31.865Z" }, - { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223, upload-time = "2025-12-10T07:08:34.166Z" }, - { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518, upload-time = "2025-12-10T07:08:36.339Z" }, - { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546, upload-time = "2025-12-10T07:08:38.128Z" }, - { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305, upload-time = "2025-12-10T07:08:41.013Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257, upload-time = "2025-12-10T07:08:42.873Z" }, - { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673, upload-time = "2025-12-10T07:08:45.362Z" }, - { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467, upload-time = "2025-12-10T07:08:47.408Z" }, - { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395, upload-time = "2025-12-10T07:08:49.337Z" }, - { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647, upload-time = "2025-12-10T07:08:51.601Z" }, -] - -[[package]] -name = "scipy" -version = "1.17.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, - { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, - { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, - { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, - { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, - { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, - { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, - { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, - { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, - { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, - { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, - { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, - { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, - { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, - { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, - { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, - { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, - { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, - { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, - { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, - { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, - { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, - { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, - { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, - { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, - { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, - { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, - { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, - { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, - { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, - { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" }, - { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" }, - { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" }, - { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" }, - { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" }, - { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" }, - { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" }, - { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" }, - { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" }, - { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" }, - { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" }, - { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" }, - { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" }, - { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" }, - { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" }, - { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" }, - { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" }, - { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, -] - -[[package]] -name = "setuptools" -version = "82.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - -[[package]] -name = "smmap" -version = "5.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" }, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, -] - -[[package]] -name = "soupsieve" -version = "2.8.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627, upload-time = "2026-01-20T04:27:02.457Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" }, -] - -[[package]] -name = "sqlalchemy" -version = "2.0.49" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/09/45/461788f35e0364a8da7bda51a1fe1b09762d0c32f12f63727998d85a873b/sqlalchemy-2.0.49.tar.gz", hash = "sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f", size = 9898221, upload-time = "2026-04-03T16:38:11.704Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/b5/e3617cc67420f8f403efebd7b043128f94775e57e5b84e7255203390ceae/sqlalchemy-2.0.49-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe", size = 2159126, upload-time = "2026-04-03T16:50:13.242Z" }, - { url = "https://files.pythonhosted.org/packages/20/9b/91ca80403b17cd389622a642699e5f6564096b698e7cdcbcbb6409898bc4/sqlalchemy-2.0.49-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014", size = 3315509, upload-time = "2026-04-03T16:54:49.332Z" }, - { url = "https://files.pythonhosted.org/packages/b1/61/0722511d98c54de95acb327824cb759e8653789af2b1944ab1cc69d32565/sqlalchemy-2.0.49-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536", size = 3315014, upload-time = "2026-04-03T16:56:56.376Z" }, - { url = "https://files.pythonhosted.org/packages/46/55/d514a653ffeb4cebf4b54c47bec32ee28ad89d39fafba16eeed1d81dccd5/sqlalchemy-2.0.49-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88", size = 3267388, upload-time = "2026-04-03T16:54:51.272Z" }, - { url = "https://files.pythonhosted.org/packages/2f/16/0dcc56cb6d3335c1671a2258f5d2cb8267c9a2260e27fde53cbfb1b3540a/sqlalchemy-2.0.49-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700", size = 3289602, upload-time = "2026-04-03T16:56:57.63Z" }, - { url = "https://files.pythonhosted.org/packages/51/6c/f8ab6fb04470a133cd80608db40aa292e6bae5f162c3a3d4ab19544a67af/sqlalchemy-2.0.49-cp311-cp311-win32.whl", hash = "sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a", size = 2119044, upload-time = "2026-04-03T17:00:53.455Z" }, - { url = "https://files.pythonhosted.org/packages/c4/59/55a6d627d04b6ebb290693681d7683c7da001eddf90b60cfcc41ee907978/sqlalchemy-2.0.49-cp311-cp311-win_amd64.whl", hash = "sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af", size = 2143642, upload-time = "2026-04-03T17:00:54.769Z" }, - { url = "https://files.pythonhosted.org/packages/49/b3/2de412451330756aaaa72d27131db6dde23995efe62c941184e15242a5fa/sqlalchemy-2.0.49-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b", size = 2157681, upload-time = "2026-04-03T16:53:07.132Z" }, - { url = "https://files.pythonhosted.org/packages/50/84/b2a56e2105bd11ebf9f0b93abddd748e1a78d592819099359aa98134a8bf/sqlalchemy-2.0.49-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982", size = 3338976, upload-time = "2026-04-03T17:07:40Z" }, - { url = "https://files.pythonhosted.org/packages/2c/fa/65fcae2ed62f84ab72cf89536c7c3217a156e71a2c111b1305ab6f0690e2/sqlalchemy-2.0.49-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672", size = 3351937, upload-time = "2026-04-03T17:12:23.374Z" }, - { url = "https://files.pythonhosted.org/packages/f8/2f/6fd118563572a7fe475925742eb6b3443b2250e346a0cc27d8d408e73773/sqlalchemy-2.0.49-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e", size = 3281646, upload-time = "2026-04-03T17:07:41.949Z" }, - { url = "https://files.pythonhosted.org/packages/c5/d7/410f4a007c65275b9cf82354adb4bb8ba587b176d0a6ee99caa16fe638f8/sqlalchemy-2.0.49-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750", size = 3316695, upload-time = "2026-04-03T17:12:25.642Z" }, - { url = "https://files.pythonhosted.org/packages/d9/95/81f594aa60ded13273a844539041ccf1e66c5a7bed0a8e27810a3b52d522/sqlalchemy-2.0.49-cp312-cp312-win32.whl", hash = "sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0", size = 2117483, upload-time = "2026-04-03T17:05:40.896Z" }, - { url = "https://files.pythonhosted.org/packages/47/9e/fd90114059175cac64e4fafa9bf3ac20584384d66de40793ae2e2f26f3bb/sqlalchemy-2.0.49-cp312-cp312-win_amd64.whl", hash = "sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4", size = 2144494, upload-time = "2026-04-03T17:05:42.282Z" }, - { url = "https://files.pythonhosted.org/packages/ae/81/81755f50eb2478eaf2049728491d4ea4f416c1eb013338682173259efa09/sqlalchemy-2.0.49-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120", size = 2154547, upload-time = "2026-04-03T16:53:08.64Z" }, - { url = "https://files.pythonhosted.org/packages/a2/bc/3494270da80811d08bcfa247404292428c4fe16294932bce5593f215cad9/sqlalchemy-2.0.49-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2", size = 3280782, upload-time = "2026-04-03T17:07:43.508Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f5/038741f5e747a5f6ea3e72487211579d8cbea5eb9827a9cbd61d0108c4bd/sqlalchemy-2.0.49-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3", size = 3297156, upload-time = "2026-04-03T17:12:27.697Z" }, - { url = "https://files.pythonhosted.org/packages/88/50/a6af0ff9dc954b43a65ca9b5367334e45d99684c90a3d3413fc19a02d43c/sqlalchemy-2.0.49-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7", size = 3228832, upload-time = "2026-04-03T17:07:45.38Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d1/5f6bdad8de0bf546fc74370939621396515e0cdb9067402d6ba1b8afbe9a/sqlalchemy-2.0.49-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33", size = 3267000, upload-time = "2026-04-03T17:12:29.657Z" }, - { url = "https://files.pythonhosted.org/packages/f7/30/ad62227b4a9819a5e1c6abff77c0f614fa7c9326e5a3bdbee90f7139382b/sqlalchemy-2.0.49-cp313-cp313-win32.whl", hash = "sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b", size = 2115641, upload-time = "2026-04-03T17:05:43.989Z" }, - { url = "https://files.pythonhosted.org/packages/17/3a/7215b1b7d6d49dc9a87211be44562077f5f04f9bb5a59552c1c8e2d98173/sqlalchemy-2.0.49-cp313-cp313-win_amd64.whl", hash = "sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148", size = 2141498, upload-time = "2026-04-03T17:05:45.7Z" }, - { url = "https://files.pythonhosted.org/packages/28/4b/52a0cb2687a9cd1648252bb257be5a1ba2c2ded20ba695c65756a55a15a4/sqlalchemy-2.0.49-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518", size = 3560807, upload-time = "2026-04-03T16:58:31.666Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d8/fda95459204877eed0458550d6c7c64c98cc50c2d8d618026737de9ed41a/sqlalchemy-2.0.49-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d", size = 3527481, upload-time = "2026-04-03T17:06:00.155Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0a/2aac8b78ac6487240cf7afef8f203ca783e8796002dc0cf65c4ee99ff8bb/sqlalchemy-2.0.49-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0", size = 3468565, upload-time = "2026-04-03T16:58:33.414Z" }, - { url = "https://files.pythonhosted.org/packages/a5/3d/ce71cfa82c50a373fd2148b3c870be05027155ce791dc9a5dcf439790b8b/sqlalchemy-2.0.49-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08", size = 3477769, upload-time = "2026-04-03T17:06:02.787Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e8/0a9f5c1f7c6f9ca480319bf57c2d7423f08d31445974167a27d14483c948/sqlalchemy-2.0.49-cp313-cp313t-win32.whl", hash = "sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d", size = 2143319, upload-time = "2026-04-03T17:02:04.328Z" }, - { url = "https://files.pythonhosted.org/packages/0e/51/fb5240729fbec73006e137c4f7a7918ffd583ab08921e6ff81a999d6517a/sqlalchemy-2.0.49-cp313-cp313t-win_amd64.whl", hash = "sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba", size = 2175104, upload-time = "2026-04-03T17:02:05.989Z" }, - { url = "https://files.pythonhosted.org/packages/55/33/bf28f618c0a9597d14e0b9ee7d1e0622faff738d44fe986ee287cdf1b8d0/sqlalchemy-2.0.49-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e", size = 2156356, upload-time = "2026-04-03T16:53:09.914Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a7/5f476227576cb8644650eff68cc35fa837d3802b997465c96b8340ced1e2/sqlalchemy-2.0.49-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a", size = 3276486, upload-time = "2026-04-03T17:07:46.9Z" }, - { url = "https://files.pythonhosted.org/packages/2e/84/efc7c0bf3a1c5eef81d397f6fddac855becdbb11cb38ff957888603014a7/sqlalchemy-2.0.49-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066", size = 3281479, upload-time = "2026-04-03T17:12:32.226Z" }, - { url = "https://files.pythonhosted.org/packages/91/68/bb406fa4257099c67bd75f3f2261b129c63204b9155de0d450b37f004698/sqlalchemy-2.0.49-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187", size = 3226269, upload-time = "2026-04-03T17:07:48.678Z" }, - { url = "https://files.pythonhosted.org/packages/67/84/acb56c00cca9f251f437cb49e718e14f7687505749ea9255d7bd8158a6df/sqlalchemy-2.0.49-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401", size = 3248260, upload-time = "2026-04-03T17:12:34.381Z" }, - { url = "https://files.pythonhosted.org/packages/56/19/6a20ea25606d1efd7bd1862149bb2a22d1451c3f851d23d887969201633f/sqlalchemy-2.0.49-cp314-cp314-win32.whl", hash = "sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5", size = 2118463, upload-time = "2026-04-03T17:05:47.093Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4f/8297e4ed88e80baa1f5aa3c484a0ee29ef3c69c7582f206c916973b75057/sqlalchemy-2.0.49-cp314-cp314-win_amd64.whl", hash = "sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5", size = 2144204, upload-time = "2026-04-03T17:05:48.694Z" }, - { url = "https://files.pythonhosted.org/packages/1f/33/95e7216df810c706e0cd3655a778604bbd319ed4f43333127d465a46862d/sqlalchemy-2.0.49-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977", size = 3565474, upload-time = "2026-04-03T16:58:35.128Z" }, - { url = "https://files.pythonhosted.org/packages/0c/a4/ed7b18d8ccf7f954a83af6bb73866f5bc6f5636f44c7731fbb741f72cc4f/sqlalchemy-2.0.49-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01", size = 3530567, upload-time = "2026-04-03T17:06:04.587Z" }, - { url = "https://files.pythonhosted.org/packages/73/a3/20faa869c7e21a827c4a2a42b41353a54b0f9f5e96df5087629c306df71e/sqlalchemy-2.0.49-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61", size = 3474282, upload-time = "2026-04-03T16:58:37.131Z" }, - { url = "https://files.pythonhosted.org/packages/b7/50/276b9a007aa0764304ad467eceb70b04822dc32092492ee5f322d559a4dc/sqlalchemy-2.0.49-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a", size = 3480406, upload-time = "2026-04-03T17:06:07.176Z" }, - { url = "https://files.pythonhosted.org/packages/e5/c3/c80fcdb41905a2df650c2a3e0337198b6848876e63d66fe9188ef9003d24/sqlalchemy-2.0.49-cp314-cp314t-win32.whl", hash = "sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158", size = 2149151, upload-time = "2026-04-03T17:02:07.281Z" }, - { url = "https://files.pythonhosted.org/packages/05/52/9f1a62feab6ed368aff068524ff414f26a6daebc7361861035ae00b05530/sqlalchemy-2.0.49-cp314-cp314t-win_amd64.whl", hash = "sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7", size = 2184178, upload-time = "2026-04-03T17:02:08.623Z" }, - { url = "https://files.pythonhosted.org/packages/e5/30/8519fdde58a7bdf155b714359791ad1dc018b47d60269d5d160d311fdc36/sqlalchemy-2.0.49-py3-none-any.whl", hash = "sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0", size = 1942158, upload-time = "2026-04-03T16:53:44.135Z" }, -] - -[[package]] -name = "st-annotated-text" -version = "4.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "htbuilder" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4d/76/c0f64a56b779bfb5c87437687a9970d12dad37b5aea2541823d0c5010818/st_annotated_text-4.0.2.tar.gz", hash = "sha256:b0134dcf734697cc3dbdb11862c4174071e7fb6f365af55a37c710d357fd88a5", size = 7915, upload-time = "2025-01-23T15:09:02.88Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/70/f29d4359ddfd1a0afa00065e4dac32bd4931f589185f5f0f499db9aee4fe/st_annotated_text-4.0.2-py3-none-any.whl", hash = "sha256:712c45821f020eccafad3a58c10b9d2d51d449f4db999a06308ecf39a753a4df", size = 9084, upload-time = "2025-01-23T15:09:00.863Z" }, -] - -[[package]] -name = "st-theme" -version = "1.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "streamlit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/31/84/3e2fd95f5acaf6c92a51e7f038f1120235c65bb0b8be9bf43aa9fb4267e5/st-theme-1.2.3.tar.gz", hash = "sha256:ca97aece1a48ded6e83fd742c27cb0851e1bce2100ab4b6c37c7b6e003b65b42", size = 73873, upload-time = "2024-05-03T19:25:50.657Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/09/cd7134f1ed5074a7d456640e7ba9a8c8e68a831837b4e7bfd9f29e5700a4/st_theme-1.2.3-py3-none-any.whl", hash = "sha256:0a54d9817dd5f8a6d7b0d071b25ae72eacf536c63a5fb97374923938021b1389", size = 75205, upload-time = "2024-05-03T19:25:47.858Z" }, -] - -[[package]] -name = "starlette" -version = "0.50.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, -] - -[[package]] -name = "streamlit" -version = "1.43.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "altair" }, - { name = "blinker" }, - { name = "cachetools" }, - { name = "click" }, - { name = "gitpython" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pandas" }, - { name = "pillow" }, - { name = "protobuf" }, - { name = "pyarrow" }, - { name = "pydeck" }, - { name = "requests" }, - { name = "tenacity" }, - { name = "toml" }, - { name = "tornado" }, - { name = "typing-extensions" }, - { name = "watchdog", marker = "sys_platform != 'darwin'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/53/c6/c2a5a9037b583d7e48c8fe4ea07f72f59c1796c57f7c01f907a0a388e40a/streamlit-1.43.2.tar.gz", hash = "sha256:f3afa2af637d00154c6a4c560d2fde256d7dc8cc1f32a53cf20570c0967841bc", size = 9345475, upload-time = "2025-03-11T20:07:42.657Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/b0/031933cb253d757dfc8791707d413ce90ab52ec7fb4524ef955070136d31/streamlit-1.43.2-py2.py3-none-any.whl", hash = "sha256:c773ff70e8dd14066e49ba4b36740e4d36b0c534624f49abcc73b75389bf9420", size = 9734394, upload-time = "2025-03-11T20:07:39.906Z" }, -] - -[[package]] -name = "streamlit-autorefresh" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "streamlit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/88/8c/e48bee687408fe563652bda4a7f2f5ef85d5a527b1883513fdcb05f1e66b/streamlit-autorefresh-1.0.1.tar.gz", hash = "sha256:a89abf23f2c4e52d37be442115cd5566b41f382e3c09ff08817e17a25f50b8ed", size = 351385, upload-time = "2023-06-25T18:58:34.889Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/82/e378f178498f1d99a672d81df71ebe9693a106cec6a628ee52ce3288cd6d/streamlit_autorefresh-1.0.1-py3-none-any.whl", hash = "sha256:8f0a772eff9d56807d19dc422e44ef92d900bbb22b1b85de31d8d82ea7d875f1", size = 700771, upload-time = "2023-06-25T18:58:33.195Z" }, -] - -[[package]] -name = "streamlit-avatar" -version = "0.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "streamlit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b7/a2/09c6046b1934f79ce2c6d767f3c7760ce632685498e67af455a2ae962d4a/streamlit_avatar-0.1.3.tar.gz", hash = "sha256:023893bd80db5a923d397fc64d27c7a972107da4f0bea0feb99a32f9363f2691", size = 402071, upload-time = "2024-06-03T06:37:38.406Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/77/4b82378aa4caf7ef7701841b25a5406b3a3bb6171c8b9eee540553843b7f/streamlit_avatar-0.1.3-py3-none-any.whl", hash = "sha256:873e6f0022635eda29d6b76e570adcbc095d8e6d23f18dd1e288720b4736364d", size = 779612, upload-time = "2024-06-03T06:37:35.671Z" }, -] - -[[package]] -name = "streamlit-camera-input-live" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "streamlit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/e1/770277a024a02916120fb396a2ec9524fba2a00d077f98295e71bbb698e7/streamlit-camera-input-live-0.2.0.tar.gz", hash = "sha256:20ceb952b98410084176fcfeb9148e02ea29033a88d4a923161ac7890cedae0f", size = 5761, upload-time = "2022-09-27T16:09:30.726Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/9b/1c0ea2b4cc39ff1be6ad7171ac9455203d08e725a9e101cc3e7311589842/streamlit_camera_input_live-0.2.0-py3-none-any.whl", hash = "sha256:dacb56cdedbb0d6c07e35a66b755b9145b5023e5c855c64193c3d3e73198e9be", size = 6586, upload-time = "2022-09-27T16:09:29.528Z" }, -] - -[[package]] -name = "streamlit-card" -version = "1.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "streamlit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/39/765f93dc09cabb8d4762714bf3a0d25892671450eda2c48e4dbdb2292d6b/streamlit_card-1.0.2.tar.gz", hash = "sha256:8001cd5edd8a6e2db36ee81f37dc645f08f78c21a2ba968403176c68b4f33cb1", size = 672326, upload-time = "2024-05-10T14:04:03.571Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/22/1d81bd6b2552c67f50a19501c00c7ee08022eacffb21c6900929359f7ce5/streamlit_card-1.0.2-py3-none-any.whl", hash = "sha256:f5d01ce57d6481eb3ba44e504146f56a7b82907d6700f0c19266ed6381a9c58f", size = 680848, upload-time = "2024-05-10T14:04:00.723Z" }, -] - -[[package]] -name = "streamlit-embedcode" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "streamlit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/ef/9c254c44bb5cb276bb2503b1b7101274cdc9f714206fb73d3ec59bec6085/streamlit-embedcode-0.1.2.tar.gz", hash = "sha256:22a50eb43407bab3d0ed2d4b58e89819da477cd0592ef87edbd373c286712e3a", size = 3567, upload-time = "2021-05-17T19:03:18.29Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/0b/5c68315b24d7565a1c51611c94124cafce5463ed869270c762552d74d96b/streamlit_embedcode-0.1.2-py3-none-any.whl", hash = "sha256:b3c9520c1b48f2eef3c702b5a967f64c9a8ff2ea8e74ebb26c0e9195965bb923", size = 3534, upload-time = "2021-05-17T19:03:17.286Z" }, -] - -[[package]] -name = "streamlit-extras" -version = "0.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "entrypoints" }, - { name = "htbuilder" }, - { name = "markdownlit" }, - { name = "plotly" }, - { name = "prometheus-client" }, - { name = "protobuf" }, - { name = "st-annotated-text" }, - { name = "st-theme" }, - { name = "streamlit" }, - { name = "streamlit-avatar" }, - { name = "streamlit-camera-input-live" }, - { name = "streamlit-card" }, - { name = "streamlit-embedcode" }, - { name = "streamlit-faker" }, - { name = "streamlit-image-coordinates" }, - { name = "streamlit-keyup" }, - { name = "streamlit-toggle-switch" }, - { name = "streamlit-vertical-slider" }, - { name = "validators" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/53/fc/a4738af08725700411fe174e120450d0f0e3b409009641a944f4baea09f7/streamlit_extras-0.6.0.tar.gz", hash = "sha256:3cfa5cf7f17428b53a1d963a8c84903a1ba6f21173364d1664d58b6a58ee91b9", size = 60068, upload-time = "2025-03-13T16:29:14.977Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/90/128e42f2f1c5c05abb6a76b5792ec97504aad77a4ba0f4bc8d73b2a43a05/streamlit_extras-0.6.0-py3-none-any.whl", hash = "sha256:b80134871046586a55dd155079c2971d2be47024b6a330fd077a7b505b81d470", size = 85260, upload-time = "2025-03-13T16:29:13.97Z" }, -] - -[[package]] -name = "streamlit-faker" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "altex" }, - { name = "faker" }, - { name = "matplotlib" }, - { name = "streamlit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/65/a9/312137533f56a634d7774eaaf0c5438322ae7c6ac84429a2cfd2d5f6f841/streamlit_faker-0.0.4.tar.gz", hash = "sha256:dc94cbfafcf035b171cb989531a1f19f4241e5b3c2e61311ed91a02ed188fed0", size = 14526, upload-time = "2025-07-01T15:14:44.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/52/19755b6a87750640a1f5232c8c80ad46df3c2afcaf25594a5884756d220c/streamlit_faker-0.0.4-py3-none-any.whl", hash = "sha256:1802e92b5f1164f1f7c640c44fd5a349415b9a7f023fa912a10e03161de08ec8", size = 14156, upload-time = "2025-07-01T15:14:36.89Z" }, -] - -[[package]] -name = "streamlit-image-coordinates" -version = "0.1.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "streamlit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/4f/270c310ec1b200914200bc8387ce166e50c247ae8e4f9e5072a8b674ba37/streamlit_image_coordinates-0.1.9.tar.gz", hash = "sha256:825e1f49053f1363913014a4e9130a03b9ca01fb5f7bd269b17afe8162d2ba37", size = 6494, upload-time = "2024-06-04T16:40:56.258Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/cf/1dba1380eb3b632f1f86c80533a3fca1376a938517044048122adf816a90/streamlit_image_coordinates-0.1.9-py3-none-any.whl", hash = "sha256:e577d475707ce8a3f7be1825027af6b4d7b609a456f4b25b794756ed2436ab06", size = 7049, upload-time = "2024-06-04T16:40:54.698Z" }, -] - -[[package]] -name = "streamlit-keyup" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "streamlit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/88/25/d153babd6d8dff72ee2e56976862ec7fdd7856ec7da2cb6ffd4b3b12f336/streamlit_keyup-0.3.0.tar.gz", hash = "sha256:8595a14892423243669e5d50e982853ffb7eb201b65952a48676133ab9bbc937", size = 7387, upload-time = "2025-01-02T14:48:40.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/ed/facce225c0d360b9bfa053c827029312e1b255e84d79320bda3539417b50/streamlit_keyup-0.3.0-py3-none-any.whl", hash = "sha256:ec7221617b1c832526db52859196c417578d6b4285942fbd10a0b2ff313899b3", size = 7493, upload-time = "2025-01-02T14:48:35.414Z" }, -] - -[[package]] -name = "streamlit-toggle-switch" -version = "1.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "streamlit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/12/ce/9b8b93bc87223b0680fb73ea3c877d4f02390da12a6a1a561b90ec438d0d/streamlit_toggle_switch-1.0.2.tar.gz", hash = "sha256:991b103cd3448b0f6507f8051777b996a17b4630956d5b6fa13344175b20e572", size = 627850, upload-time = "2022-10-25T22:57:42.206Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/bc/f3ffd4143379756c494b8cb5faf1fffae48b722fe7b05794068d05bec965/streamlit_toggle_switch-1.0.2-py3-none-any.whl", hash = "sha256:0081212d80d178bda337acf2432425e2016d757f57834b18645d4c5b928d4c0f", size = 635443, upload-time = "2022-10-25T22:57:40.055Z" }, -] - -[[package]] -name = "streamlit-vertical-slider" -version = "2.5.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "streamlit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/29/b6/c4596692276174e01899e4da3bdcef101451bc45f1a738aad54a0e60bcfe/streamlit_vertical_slider-2.5.5.tar.gz", hash = "sha256:d6854cf81a606f5c021df2037d2c49036df2d03ce5082a5227a2acca8322ca74", size = 635192, upload-time = "2023-12-22T03:04:41.177Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/3f/f16875fa650cc6f6c1bc19ef9ccae5a1a7f434ec08747181743b48c31830/streamlit_vertical_slider-2.5.5-py3-none-any.whl", hash = "sha256:8182e861444fcd69e05c05e7109a636d459560c249f1addf78b58e525a719cb6", size = 1769408, upload-time = "2023-12-22T03:04:38.527Z" }, -] - -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, -] - -[[package]] -name = "tenacity" -version = "9.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413, upload-time = "2026-02-07T10:45:33.841Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926, upload-time = "2026-02-07T10:45:32.24Z" }, -] - -[[package]] -name = "threadpoolctl" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, -] - -[[package]] -name = "tokenizers" -version = "0.22.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, - { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, - { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, - { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, - { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, - { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, - { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, - { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, - { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, - { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, - { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, - { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, -] - -[[package]] -name = "toml" -version = "0.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, -] - -[[package]] -name = "tornado" -version = "6.5.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/f1/3173dfa4a18db4a9b03e5d55325559dab51ee653763bb8745a75af491286/tornado-6.5.5.tar.gz", hash = "sha256:192b8f3ea91bd7f1f50c06955416ed76c6b72f96779b962f07f911b91e8d30e9", size = 516006, upload-time = "2026-03-10T21:31:02.067Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:487dc9cc380e29f58c7ab88f9e27cdeef04b2140862e5076a66fb6bb68bb1bfa", size = 445983, upload-time = "2026-03-10T21:30:44.28Z" }, - { url = "https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:65a7f1d46d4bb41df1ac99f5fcb685fb25c7e61613742d5108b010975a9a6521", size = 444246, upload-time = "2026-03-10T21:30:46.571Z" }, - { url = "https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e74c92e8e65086b338fd56333fb9a68b9f6f2fe7ad532645a290a464bcf46be5", size = 447229, upload-time = "2026-03-10T21:30:48.273Z" }, - { url = "https://files.pythonhosted.org/packages/34/01/74e034a30ef59afb4097ef8659515e96a39d910b712a89af76f5e4e1f93c/tornado-6.5.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:435319e9e340276428bbdb4e7fa732c2d399386d1de5686cb331ec8eee754f07", size = 448192, upload-time = "2026-03-10T21:30:51.22Z" }, - { url = "https://files.pythonhosted.org/packages/be/00/fe9e02c5a96429fce1a1d15a517f5d8444f9c412e0bb9eadfbe3b0fc55bf/tornado-6.5.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3f54aa540bdbfee7b9eb268ead60e7d199de5021facd276819c193c0fb28ea4e", size = 448039, upload-time = "2026-03-10T21:30:53.52Z" }, - { url = "https://files.pythonhosted.org/packages/82/9e/656ee4cec0398b1d18d0f1eb6372c41c6b889722641d84948351ae19556d/tornado-6.5.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36abed1754faeb80fbd6e64db2758091e1320f6bba74a4cf8c09cd18ccce8aca", size = 447445, upload-time = "2026-03-10T21:30:55.541Z" }, - { url = "https://files.pythonhosted.org/packages/5a/76/4921c00511f88af86a33de770d64141170f1cfd9c00311aea689949e274e/tornado-6.5.5-cp39-abi3-win32.whl", hash = "sha256:dd3eafaaeec1c7f2f8fdcd5f964e8907ad788fe8a5a32c4426fbbdda621223b7", size = 448582, upload-time = "2026-03-10T21:30:57.142Z" }, - { url = "https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl", hash = "sha256:6443a794ba961a9f619b1ae926a2e900ac20c34483eea67be4ed8f1e58d3ef7b", size = 448990, upload-time = "2026-03-10T21:30:58.857Z" }, - { url = "https://files.pythonhosted.org/packages/b7/c8/876602cbc96469911f0939f703453c1157b0c826ecb05bdd32e023397d4e/tornado-6.5.5-cp39-abi3-win_arm64.whl", hash = "sha256:2c9a876e094109333f888539ddb2de4361743e5d21eece20688e3e351e4990a6", size = 448016, upload-time = "2026-03-10T21:31:00.43Z" }, -] - -[[package]] -name = "tqdm" -version = "4.67.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, -] - -[[package]] -name = "turbopuffer" -version = "1.21.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "orjson" }, - { name = "pybase64" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/17/e5/c7df3bde9efbbafdcf10ba5ae2a0159e4a894183393e2dc02d2cb1c19792/turbopuffer-1.21.0.tar.gz", hash = "sha256:988ddfceab082190673a097edb1e32636a8958cc6ad6c5260b2990513c12ded9", size = 337493, upload-time = "2026-04-07T15:47:34.586Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/30/995ae0edbb748d6a98bcbb0040ed354e2df31a06092a60e986763de52893/turbopuffer-1.21.0-py3-none-any.whl", hash = "sha256:adbea8fc118138e311047571f1438b4a2ce0d67aa759c1dd33fc3b90bdf2c424", size = 122566, upload-time = "2026-04-07T15:47:35.941Z" }, -] - -[[package]] -name = "typer" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - -[[package]] -name = "tzdata" -version = "2026.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/f5/cd531b2d15a671a40c0f66cf06bc3570a12cd56eef98960068ebbad1bf5a/tzdata-2026.1.tar.gz", hash = "sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98", size = 197639, upload-time = "2026-04-03T11:25:22.002Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/70/d460bd685a170790ec89317e9bd33047988e4bce507b831f5db771e142de/tzdata-2026.1-py2.py3-none-any.whl", hash = "sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9", size = 348952, upload-time = "2026-04-03T11:25:20.313Z" }, -] - -[[package]] -name = "ujson" -version = "5.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/3e/c35530c5ffc25b71c59ae0cd7b8f99df37313daa162ce1e2f7925f7c2877/ujson-5.12.0.tar.gz", hash = "sha256:14b2e1eb528d77bc0f4c5bd1a7ebc05e02b5b41beefb7e8567c9675b8b13bcf4", size = 7158451, upload-time = "2026-03-11T22:19:30.397Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/22/fd22e2f6766bae934d3050517ca47d463016bd8688508d1ecc1baa18a7ad/ujson-5.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58a11cb49482f1a095a2bd9a1d81dd7c8fb5d2357f959ece85db4e46a825fd00", size = 56139, upload-time = "2026-03-11T22:18:04.591Z" }, - { url = "https://files.pythonhosted.org/packages/c6/fd/6839adff4fc0164cbcecafa2857ba08a6eaeedd7e098d6713cb899a91383/ujson-5.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9b3cf13facf6f77c283af0e1713e5e8c47a0fe295af81326cb3cb4380212e797", size = 53836, upload-time = "2026-03-11T22:18:05.662Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b0/0c19faac62d68ceeffa83a08dc3d71b8462cf5064d0e7e0b15ba19898dad/ujson-5.12.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb94245a715b4d6e24689de12772b85329a1f9946cbf6187923a64ecdea39e65", size = 57851, upload-time = "2026-03-11T22:18:06.744Z" }, - { url = "https://files.pythonhosted.org/packages/04/f6/e7fd283788de73b86e99e08256726bb385923249c21dcd306e59d532a1a1/ujson-5.12.0-cp311-cp311-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:0fe6b8b8968e11dd9b2348bd508f0f57cf49ab3512064b36bc4117328218718e", size = 59906, upload-time = "2026-03-11T22:18:07.791Z" }, - { url = "https://files.pythonhosted.org/packages/d7/3a/b100735a2b43ee6e8fe4c883768e362f53576f964d4ea841991060aeaf35/ujson-5.12.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89e302abd3749f6d6699691747969a5d85f7c73081d5ed7e2624c7bd9721a2ab", size = 57409, upload-time = "2026-03-11T22:18:08.79Z" }, - { url = "https://files.pythonhosted.org/packages/5c/fa/f97cc20c99ca304662191b883ae13ae02912ca7244710016ba0cb8a5be34/ujson-5.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0727363b05ab05ee737a28f6200dc4078bce6b0508e10bd8aab507995a15df61", size = 1037339, upload-time = "2026-03-11T22:18:10.424Z" }, - { url = "https://files.pythonhosted.org/packages/10/7a/53ddeda0ffe1420db2f9999897b3cbb920fbcff1849d1f22b196d0f34785/ujson-5.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b62cb9a7501e1f5c9ffe190485501349c33e8862dde4377df774e40b8166871f", size = 1196625, upload-time = "2026-03-11T22:18:11.82Z" }, - { url = "https://files.pythonhosted.org/packages/0d/1a/4c64a6bef522e9baf195dd5be151bc815cd4896c50c6e2489599edcda85f/ujson-5.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a6ec5bf6bc361f2f0f9644907a36ce527715b488988a8df534120e5c34eeda94", size = 1089669, upload-time = "2026-03-11T22:18:13.343Z" }, - { url = "https://files.pythonhosted.org/packages/18/11/8ccb109f5777ec0d9fb826695a9e2ac36ae94c1949fc8b1e4d23a5bd067a/ujson-5.12.0-cp311-cp311-win32.whl", hash = "sha256:006428d3813b87477d72d306c40c09f898a41b968e57b15a7d88454ecc42a3fb", size = 39648, upload-time = "2026-03-11T22:18:14.785Z" }, - { url = "https://files.pythonhosted.org/packages/6f/e3/87fc4c27b20d5125cff7ce52d17ea7698b22b74426da0df238e3efcb0cf2/ujson-5.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:40aa43a7a3a8d2f05e79900858053d697a88a605e3887be178b43acbcd781161", size = 43876, upload-time = "2026-03-11T22:18:15.768Z" }, - { url = "https://files.pythonhosted.org/packages/9e/21/324f0548a8c8c48e3e222eaed15fb6d48c796593002b206b4a28a89e445f/ujson-5.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:561f89cc82deeae82e37d4a4764184926fb432f740a9691563a391b13f7339a4", size = 38553, upload-time = "2026-03-11T22:18:17.251Z" }, - { url = "https://files.pythonhosted.org/packages/84/f6/ac763d2108d28f3a40bb3ae7d2fafab52ca31b36c2908a4ad02cd3ceba2a/ujson-5.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:09b4beff9cc91d445d5818632907b85fb06943b61cb346919ce202668bf6794a", size = 56326, upload-time = "2026-03-11T22:18:18.467Z" }, - { url = "https://files.pythonhosted.org/packages/25/46/d0b3af64dcdc549f9996521c8be6d860ac843a18a190ffc8affeb7259687/ujson-5.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca0c7ce828bb76ab78b3991904b477c2fd0f711d7815c252d1ef28ff9450b052", size = 53910, upload-time = "2026-03-11T22:18:19.502Z" }, - { url = "https://files.pythonhosted.org/packages/9a/10/853c723bcabc3e9825a079019055fc99e71b85c6bae600607a2b9d31d18d/ujson-5.12.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2d79c6635ccffcbfc1d5c045874ba36b594589be81d50d43472570bb8de9c57", size = 57754, upload-time = "2026-03-11T22:18:20.874Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c6/6e024830d988f521f144ead641981c1f7a82c17ad1927c22de3242565f5c/ujson-5.12.0-cp312-cp312-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:7e07f6f644d2c44d53b7a320a084eef98063651912c1b9449b5f45fcbdc6ccd2", size = 59936, upload-time = "2026-03-11T22:18:21.924Z" }, - { url = "https://files.pythonhosted.org/packages/34/c9/c5f236af5abe06b720b40b88819d00d10182d2247b1664e487b3ed9229cf/ujson-5.12.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:085b6ce182cdd6657481c7c4003a417e0655c4f6e58b76f26ee18f0ae21db827", size = 57463, upload-time = "2026-03-11T22:18:22.924Z" }, - { url = "https://files.pythonhosted.org/packages/ae/04/41342d9ef68e793a87d84e4531a150c2b682f3bcedfe59a7a5e3f73e9213/ujson-5.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:16b4fe9c97dc605f5e1887a9e1224287291e35c56cbc379f8aa44b6b7bcfe2bb", size = 1037239, upload-time = "2026-03-11T22:18:24.04Z" }, - { url = "https://files.pythonhosted.org/packages/d4/81/dc2b7617d5812670d4ff4a42f6dd77926430ee52df0dedb2aec7990b2034/ujson-5.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0d2e8db5ade3736a163906154ca686203acc7d1d30736cbf577c730d13653d84", size = 1196713, upload-time = "2026-03-11T22:18:25.391Z" }, - { url = "https://files.pythonhosted.org/packages/b6/9c/80acff0504f92459ed69e80a176286e32ca0147ac6a8252cd0659aad3227/ujson-5.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93bc91fdadcf046da37a214eaa714574e7e9b1913568e93bb09527b2ceb7f759", size = 1089742, upload-time = "2026-03-11T22:18:26.738Z" }, - { url = "https://files.pythonhosted.org/packages/e3/f0/123ffaac17e45ef2b915e3e3303f8f4ea78bb8d42afad828844e08622b1e/ujson-5.12.0-cp312-cp312-win32.whl", hash = "sha256:2a248750abce1c76fbd11b2e1d88b95401e72819295c3b851ec73399d6849b3d", size = 39773, upload-time = "2026-03-11T22:18:28.244Z" }, - { url = "https://files.pythonhosted.org/packages/b5/20/f3bd2b069c242c2b22a69e033bfe224d1d15d3649e6cd7cc7085bb1412ff/ujson-5.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:1b5c6ceb65fecd28a1d20d1eba9dbfa992612b86594e4b6d47bb580d2dd6bcb3", size = 44040, upload-time = "2026-03-11T22:18:29.236Z" }, - { url = "https://files.pythonhosted.org/packages/f0/a7/01b5a0bcded14cd2522b218f2edc3533b0fcbccdea01f3e14a2b699071aa/ujson-5.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:9a5fcbe7b949f2e95c47ea8a80b410fcdf2da61c98553b45a4ee875580418b68", size = 38526, upload-time = "2026-03-11T22:18:30.551Z" }, - { url = "https://files.pythonhosted.org/packages/3f/f1/0ef0eeab1db8493e1833c8b440fe32cf7538f7afa6e7f7c7e9f62cef464d/ujson-5.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:15d416440148f3e56b9b244fdaf8a09fcf5a72e4944b8e119f5bf60417a2bfc8", size = 56331, upload-time = "2026-03-11T22:18:31.539Z" }, - { url = "https://files.pythonhosted.org/packages/b0/2f/9159f6f399b3f572d20847a2b80d133e3a03c14712b0da4971a36879fb64/ujson-5.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0dd3676ea0837cd70ea1879765e9e9f6be063be0436de9b3ea4b775caf83654", size = 53910, upload-time = "2026-03-11T22:18:32.829Z" }, - { url = "https://files.pythonhosted.org/packages/e5/a9/f96376818d71495d1a4be19a0ab6acf0cc01dd8826553734c3d4dac685b2/ujson-5.12.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7bbf05c38debc90d1a195b11340cc85cb43ab3e753dc47558a3a84a38cbc72da", size = 57757, upload-time = "2026-03-11T22:18:33.866Z" }, - { url = "https://files.pythonhosted.org/packages/98/8d/dd4a151caac6fdcb77f024fbe7f09d465ebf347a628ed6dd581a0a7f6364/ujson-5.12.0-cp313-cp313-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:3c2f947e55d3c7cfe124dd4521ee481516f3007d13c6ad4bf6aeb722e190eb1b", size = 59940, upload-time = "2026-03-11T22:18:35.276Z" }, - { url = "https://files.pythonhosted.org/packages/c7/17/0d36c2fee0a8d8dc37b011ccd5bbdcfaff8b8ec2bcfc5be998661cdc935b/ujson-5.12.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ea6206043385343aff0b7da65cf73677f6f5e50de8f1c879e557f4298cac36a", size = 57465, upload-time = "2026-03-11T22:18:36.644Z" }, - { url = "https://files.pythonhosted.org/packages/8c/04/b0ee4a4b643a01ba398441da1e357480595edb37c6c94c508dbe0eb9eb60/ujson-5.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bb349dbba57c76eec25e5917e07f35aabaf0a33b9e67fc13d188002500106487", size = 1037236, upload-time = "2026-03-11T22:18:37.743Z" }, - { url = "https://files.pythonhosted.org/packages/2d/08/0e7780d0bbb48fe57ded91f550144bcc99c03b5360bf2886dd0dae0ea8f5/ujson-5.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:937794042342006f707837f38d721426b11b0774d327a2a45c0bd389eb750a87", size = 1196717, upload-time = "2026-03-11T22:18:39.101Z" }, - { url = "https://files.pythonhosted.org/packages/ba/4c/e0e34107715bb4dd2d4dcc1ce244d2f074638837adf38aff85a37506efe4/ujson-5.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6ad57654570464eb1b040b5c353dee442608e06cff9102b8fcb105565a44c9ed", size = 1089748, upload-time = "2026-03-11T22:18:40.473Z" }, - { url = "https://files.pythonhosted.org/packages/72/43/814f4e2b5374d0d505c254ba4bed43eb25d2d046f19f5fd88555f81a7bd0/ujson-5.12.0-cp313-cp313-win32.whl", hash = "sha256:76bf3e7406cf23a3e1ca6a23fb1fb9ea82f4f6bd226fe226e09146b0194f85dc", size = 39778, upload-time = "2026-03-11T22:18:41.791Z" }, - { url = "https://files.pythonhosted.org/packages/0f/fe/19310d848ebe93315b6cb171277e4ce29f47ef9d46caabd63ff05d5be548/ujson-5.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:15e555c4caca42411270b2ed2b2ebc7b3a42bb04138cef6c956e1f1d49709fe2", size = 44038, upload-time = "2026-03-11T22:18:43.094Z" }, - { url = "https://files.pythonhosted.org/packages/3f/e4/7a39103d7634691601a02bd1ca7268fba4da47ed586365e6ee68168f575a/ujson-5.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bd03472c36fa3a386a6deb887113b9e3fa40efba8203eb4fe786d3c0ccc724f6", size = 38529, upload-time = "2026-03-11T22:18:44.167Z" }, - { url = "https://files.pythonhosted.org/packages/10/bd/9a8d693254bada62bfea75a507e014afcfdb6b9d047b6f8dd134bfefaf67/ujson-5.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85833bca01aa5cae326ac759276dc175c5fa3f7b3733b7d543cf27f2df12d1ef", size = 56499, upload-time = "2026-03-11T22:18:45.431Z" }, - { url = "https://files.pythonhosted.org/packages/bd/2d/285a83df8176e18dcd675d1a4cff8f7620f003f30903ea43929406e98986/ujson-5.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d22cad98c2a10bbf6aa083a8980db6ed90d4285a841c4de892890c2b28286ef9", size = 53998, upload-time = "2026-03-11T22:18:47.184Z" }, - { url = "https://files.pythonhosted.org/packages/bf/8b/e2f09e16dabfa91f6a84555df34a4329fa7621e92ed054d170b9054b9bb2/ujson-5.12.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99cc80facad240b0c2fb5a633044420878aac87a8e7c348b9486450cba93f27c", size = 57783, upload-time = "2026-03-11T22:18:48.271Z" }, - { url = "https://files.pythonhosted.org/packages/68/fb/ba1d06f3658a0c36d0ab3869ec3914f202bad0a9bde92654e41516c7bb13/ujson-5.12.0-cp314-cp314-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:d1831c07bd4dce53c4b666fa846c7eba4b7c414f2e641a4585b7f50b72f502dc", size = 60011, upload-time = "2026-03-11T22:18:49.284Z" }, - { url = "https://files.pythonhosted.org/packages/64/2b/3e322bf82d926d9857206cd5820438d78392d1f523dacecb8bd899952f73/ujson-5.12.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e00cec383eab2406c9e006bd4edb55d284e94bb943fda558326048178d26961", size = 57465, upload-time = "2026-03-11T22:18:50.584Z" }, - { url = "https://files.pythonhosted.org/packages/e9/fd/af72d69603f9885e5136509a529a4f6d88bf652b457263ff96aefcd3ab7d/ujson-5.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f19b3af31d02a2e79c5f9a6deaab0fb3c116456aeb9277d11720ad433de6dfc6", size = 1037275, upload-time = "2026-03-11T22:18:51.998Z" }, - { url = "https://files.pythonhosted.org/packages/9c/a7/a2411ec81aef7872578e56304c3e41b3a544a9809e95c8e1df46923fc40b/ujson-5.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:bacbd3c69862478cbe1c7ed4325caedec580d8acf31b8ee1b9a1e02a56295cad", size = 1196758, upload-time = "2026-03-11T22:18:53.548Z" }, - { url = "https://files.pythonhosted.org/packages/ed/85/aa18ae175dd03a118555aa14304d4f466f9db61b924c97c6f84388ecacb1/ujson-5.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94c5f1621cbcab83c03be46441f090b68b9f307b6c7ec44d4e3f6d5997383df4", size = 1089760, upload-time = "2026-03-11T22:18:55.336Z" }, - { url = "https://files.pythonhosted.org/packages/d3/d4/4b40b67ac7e916ebffc3041ae2320c5c0b8a045300d4c542b6e50930cca5/ujson-5.12.0-cp314-cp314-win32.whl", hash = "sha256:e6369ac293d2cc40d52577e4fa3d75a70c1aae2d01fa3580a34a4e6eff9286b9", size = 41043, upload-time = "2026-03-11T22:18:56.505Z" }, - { url = "https://files.pythonhosted.org/packages/24/38/a1496d2a3428981f2b3a2ffbb4656c2b05be6cc406301d6b10a6445f6481/ujson-5.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:31348a0ffbfc815ce78daac569d893349d85a0b57e1cd2cdbba50b7f333784da", size = 45303, upload-time = "2026-03-11T22:18:57.454Z" }, - { url = "https://files.pythonhosted.org/packages/85/d3/39dbd3159543d9c57ec3a82d36226152cf0d710784894ce5aa24b8220ac1/ujson-5.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:6879aed770557f0961b252648d36f6fdaab41079d37a2296b5649fd1b35608e0", size = 39860, upload-time = "2026-03-11T22:18:58.578Z" }, - { url = "https://files.pythonhosted.org/packages/c3/71/9b4dacb177d3509077e50497222d39eec04c8b41edb1471efc764d645237/ujson-5.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7ddb08b3c2f9213df1f2e3eb2fbea4963d80ec0f8de21f0b59898e34f3b3d96d", size = 56845, upload-time = "2026-03-11T22:18:59.629Z" }, - { url = "https://files.pythonhosted.org/packages/24/c2/8abffa3be1f3d605c4a62445fab232b3e7681512ce941c6b23014f404d36/ujson-5.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0a3ae28f0b209be5af50b54ca3e2123a3de3a57d87b75f1e5aa3d7961e041983", size = 54463, upload-time = "2026-03-11T22:19:00.697Z" }, - { url = "https://files.pythonhosted.org/packages/db/2e/60114a35d1d6796eb428f7affcba00a921831ff604a37d9142c3d8bbe5c5/ujson-5.12.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30ad4359413c8821cc7b3707f7ca38aa8bc852ba3b9c5a759ee2d7740157315", size = 58689, upload-time = "2026-03-11T22:19:01.739Z" }, - { url = "https://files.pythonhosted.org/packages/c8/ad/010925c2116c21ce119f9c2ff18d01f48a19ade3ff4c5795da03ce5829fc/ujson-5.12.0-cp314-cp314t-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:02f93da7a4115e24f886b04fd56df1ee8741c2ce4ea491b7ab3152f744ad8f8e", size = 60618, upload-time = "2026-03-11T22:19:03.101Z" }, - { url = "https://files.pythonhosted.org/packages/9b/74/db7f638bf20282b1dccf454386cbd483faaaed3cdbb9cb27e06f74bb109e/ujson-5.12.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ff4ede90ed771140caa7e1890de17431763a483c54b3c1f88bd30f0cc1affc0", size = 58151, upload-time = "2026-03-11T22:19:04.175Z" }, - { url = "https://files.pythonhosted.org/packages/9c/7e/3ebaecfa70a2e8ce623db8e21bd5cb05d42a5ef943bcbb3309d71b5de68d/ujson-5.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bf9cc97f05048ac8f3e02cd58f0fe62b901453c24345bfde287f4305dcc31c", size = 1038117, upload-time = "2026-03-11T22:19:05.558Z" }, - { url = "https://files.pythonhosted.org/packages/2e/aa/e073eda7f0036c2973b28db7bb99faba17a932e7b52d801f9bb3e726271f/ujson-5.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2324d9a0502317ffc35d38e153c1b2fa9610ae03775c9d0f8d0cca7b8572b04e", size = 1197434, upload-time = "2026-03-11T22:19:06.92Z" }, - { url = "https://files.pythonhosted.org/packages/1c/01/b9a13f058fdd50c746b192c4447ca8d6352e696dcda912ccee10f032ff85/ujson-5.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:50524f4f6a1c839714dbaff5386a1afb245d2d5ec8213a01fbc99cea7307811e", size = 1090401, upload-time = "2026-03-11T22:19:08.383Z" }, - { url = "https://files.pythonhosted.org/packages/c4/37/3d1b4e0076b6e43379600b5229a5993db8a759ff2e1830ea635d876f6644/ujson-5.12.0-cp314-cp314t-win32.whl", hash = "sha256:f7a0430d765f9bda043e6aefaba5944d5f21ec43ff4774417d7e296f61917382", size = 41880, upload-time = "2026-03-11T22:19:09.671Z" }, - { url = "https://files.pythonhosted.org/packages/b1/c5/3c2a262a138b9f0014fe1134a6b5fdc2c54245030affbaac2fcbc0632138/ujson-5.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:ccbfd94e59aad4a2566c71912b55f0547ac1680bfac25eb138e6703eb3dd434e", size = 46365, upload-time = "2026-03-11T22:19:10.662Z" }, - { url = "https://files.pythonhosted.org/packages/83/40/956dc20b7e00dc0ff3259871864f18dab211837fce3478778bedb3132ac1/ujson-5.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:42d875388fbd091c7ea01edfff260f839ba303038ffb23475ef392012e4d63dd", size = 40398, upload-time = "2026-03-11T22:19:11.666Z" }, - { url = "https://files.pythonhosted.org/packages/95/3c/5ee154d505d1aad2debc4ba38b1a60ae1949b26cdb5fa070e85e320d6b64/ujson-5.12.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:bf85a00ac3b56a1e7a19c5be7b02b5180a0895ac4d3c234d717a55e86960691c", size = 54494, upload-time = "2026-03-11T22:19:13.035Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b3/9496ec399ec921e434a93b340bd5052999030b7ac364be4cbe5365ac6b20/ujson-5.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:64df53eef4ac857eb5816a56e2885ccf0d7dff6333c94065c93b39c51063e01d", size = 57999, upload-time = "2026-03-11T22:19:14.385Z" }, - { url = "https://files.pythonhosted.org/packages/0e/da/e9ae98133336e7c0d50b43626c3f2327937cecfa354d844e02ac17379ed1/ujson-5.12.0-graalpy312-graalpy250_312_native-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c0aed6a4439994c9666fb8a5b6c4eac94d4ef6ddc95f9b806a599ef83547e3b", size = 54518, upload-time = "2026-03-11T22:19:15.4Z" }, - { url = "https://files.pythonhosted.org/packages/58/10/978d89dded6bb1558cd46ba78f4351198bd2346db8a8ee1a94119022ce40/ujson-5.12.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efae5df7a8cc8bdb1037b0f786b044ce281081441df5418c3a0f0e1f86fe7bb3", size = 55736, upload-time = "2026-03-11T22:19:16.496Z" }, - { url = "https://files.pythonhosted.org/packages/80/25/1df8e6217c92e57a1266bf5be750b1dddc126ee96e53fe959d5693503bc6/ujson-5.12.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:8712b61eb1b74a4478cfd1c54f576056199e9f093659334aeb5c4a6b385338e5", size = 44615, upload-time = "2026-03-11T22:19:17.53Z" }, - { url = "https://files.pythonhosted.org/packages/19/fa/f4a957dddb99bd68c8be91928c0b6fefa7aa8aafc92c93f5d1e8b32f6702/ujson-5.12.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:871c0e5102e47995b0e37e8df7819a894a6c3da0d097545cd1f9f1f7d7079927", size = 52145, upload-time = "2026-03-11T22:19:18.566Z" }, - { url = "https://files.pythonhosted.org/packages/55/6e/50b5cf612de1ca06c7effdc5a5d7e815774dee85a5858f1882c425553b82/ujson-5.12.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:56ba3f7abbd6b0bb282a544dc38406d1a188d8bb9164f49fdb9c2fee62cb29da", size = 49577, upload-time = "2026-03-11T22:19:19.627Z" }, - { url = "https://files.pythonhosted.org/packages/6e/24/b6713fa9897774502cd4c2d6955bb4933349f7d84c3aa805531c382a4209/ujson-5.12.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c5a52987a990eb1bae55f9000994f1afdb0326c154fb089992f839ab3c30688", size = 50807, upload-time = "2026-03-11T22:19:20.778Z" }, - { url = "https://files.pythonhosted.org/packages/1f/b6/c0e0f7901180ef80d16f3a4bccb5dc8b01515a717336a62928963a07b80b/ujson-5.12.0-pp311-pypy311_pp73-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:adf28d13a33f9d750fe7a78fb481cac298fa257d8863d8727b2ea4455ea41235", size = 56972, upload-time = "2026-03-11T22:19:21.84Z" }, - { url = "https://files.pythonhosted.org/packages/02/a9/05d91b4295ea7239151eb08cf240e5a2ba969012fda50bc27bcb1ea9cd71/ujson-5.12.0-pp311-pypy311_pp73-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51acc750ec7a2df786cdc868fb16fa04abd6269a01d58cf59bafc57978773d8e", size = 52045, upload-time = "2026-03-11T22:19:22.879Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7a/92047d32bf6f2d9db64605fc32e8eb0e0dd68b671eaafc12a464f69c4af4/ujson-5.12.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:ab9056d94e5db513d9313b34394f3a3b83e6301a581c28ad67773434f3faccab", size = 44053, upload-time = "2026-03-11T22:19:23.918Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "uvicorn" -version = "0.44.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/da/6eee1ff8b6cbeed47eeb5229749168e81eb4b7b999a1a15a7176e51410c9/uvicorn-0.44.0.tar.gz", hash = "sha256:6c942071b68f07e178264b9152f1f16dfac5da85880c4ce06366a96d70d4f31e", size = 86947, upload-time = "2026-04-06T09:23:22.826Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/23/a5bbd9600dd607411fa644c06ff4951bec3a4d82c4b852374024359c19c0/uvicorn-0.44.0-py3-none-any.whl", hash = "sha256:ce937c99a2cc70279556967274414c087888e8cec9f9c94644dfca11bd3ced89", size = 69425, upload-time = "2026-04-06T09:23:21.524Z" }, -] - -[package.optional-dependencies] -standard = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "httptools" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, - { name = "watchfiles" }, - { name = "websockets" }, -] - -[[package]] -name = "uvloop" -version = "0.22.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/d5/69900f7883235562f1f50d8184bb7dd84a2fb61e9ec63f3782546fdbd057/uvloop-0.22.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c60ebcd36f7b240b30788554b6f0782454826a0ed765d8430652621b5de674b9", size = 1352420, upload-time = "2025-10-16T22:16:21.187Z" }, - { url = "https://files.pythonhosted.org/packages/a8/73/c4e271b3bce59724e291465cc936c37758886a4868787da0278b3b56b905/uvloop-0.22.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b7f102bf3cb1995cfeaee9321105e8f5da76fdb104cdad8986f85461a1b7b77", size = 748677, upload-time = "2025-10-16T22:16:22.558Z" }, - { url = "https://files.pythonhosted.org/packages/86/94/9fb7fad2f824d25f8ecac0d70b94d0d48107ad5ece03769a9c543444f78a/uvloop-0.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53c85520781d84a4b8b230e24a5af5b0778efdb39142b424990ff1ef7c48ba21", size = 3753819, upload-time = "2025-10-16T22:16:23.903Z" }, - { url = "https://files.pythonhosted.org/packages/74/4f/256aca690709e9b008b7108bc85fba619a2bc37c6d80743d18abad16ee09/uvloop-0.22.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56a2d1fae65fd82197cb8c53c367310b3eabe1bbb9fb5a04d28e3e3520e4f702", size = 3804529, upload-time = "2025-10-16T22:16:25.246Z" }, - { url = "https://files.pythonhosted.org/packages/7f/74/03c05ae4737e871923d21a76fe28b6aad57f5c03b6e6bfcfa5ad616013e4/uvloop-0.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40631b049d5972c6755b06d0bfe8233b1bd9a8a6392d9d1c45c10b6f9e9b2733", size = 3621267, upload-time = "2025-10-16T22:16:26.819Z" }, - { url = "https://files.pythonhosted.org/packages/75/be/f8e590fe61d18b4a92070905497aec4c0e64ae1761498cad09023f3f4b3e/uvloop-0.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:535cc37b3a04f6cd2c1ef65fa1d370c9a35b6695df735fcff5427323f2cd5473", size = 3723105, upload-time = "2025-10-16T22:16:28.252Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, - { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, - { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, - { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, - { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, - { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, - { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, - { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, - { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, - { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, - { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, - { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, - { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, - { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, - { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, - { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, - { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, - { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, - { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, - { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, - { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, -] - -[[package]] -name = "validators" -version = "0.35.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/66/a435d9ae49850b2f071f7ebd8119dd4e84872b01630d6736761e6e7fd847/validators-0.35.0.tar.gz", hash = "sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a", size = 73399, upload-time = "2025-05-01T05:42:06.7Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/6e/3e955517e22cbdd565f2f8b2e73d52528b14b8bcfdb04f62466b071de847/validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd", size = 44712, upload-time = "2025-05-01T05:42:04.203Z" }, -] - -[[package]] -name = "vectordb-bench" -source = { editable = "." } -dependencies = [ - { name = "click" }, - { name = "environs" }, - { name = "hdrhistogram" }, - { name = "oss2" }, - { name = "plotly" }, - { name = "polars" }, - { name = "psutil" }, - { name = "pydantic" }, - { name = "pymilvus" }, - { name = "pytz" }, - { name = "s3fs" }, - { name = "scikit-learn" }, - { name = "streamlit" }, - { name = "streamlit-autorefresh" }, - { name = "streamlit-extras" }, - { name = "tqdm" }, - { name = "ujson" }, -] - -[package.optional-dependencies] -alisql = [ - { name = "mysql-connector-python" }, -] -aliyun-opensearch = [ - { name = "alibabacloud-ha3engine-vector" }, -] -all = [ - { name = "alibabacloud-ha3engine-vector" }, - { name = "chromadb" }, - { name = "clickhouse-connect" }, - { name = "elasticsearch" }, - { name = "endee" }, - { name = "grpcio" }, - { name = "grpcio-tools" }, - { name = "lancedb" }, - { name = "mariadb" }, - { name = "memorydb" }, - { name = "mysql-connector-python" }, - { name = "opensearch-dsl" }, - { name = "opensearch-py" }, - { name = "pgvecto-rs", extra = ["psycopg3"] }, - { name = "pgvector" }, - { name = "pinecone" }, - { name = "psycopg" }, - { name = "psycopg-binary" }, - { name = "pymysql" }, - { name = "pyvespa" }, - { name = "qdrant-client" }, - { name = "redis" }, - { name = "sqlalchemy" }, - { name = "turbopuffer" }, - { name = "weaviate-client" }, - { name = "zvec" }, -] -antfly = [ - { name = "httpx" }, -] -chromadb = [ - { name = "chromadb" }, -] -clickhouse = [ - { name = "clickhouse-connect" }, -] -cockroachdb = [ - { name = "pgvector" }, - { name = "psycopg", extra = ["binary", "pool"] }, -] -doris = [ - { name = "doris-vector-search" }, -] -elastic = [ - { name = "elasticsearch" }, -] -endee = [ - { name = "endee" }, -] -lancedb = [ - { name = "lancedb" }, -] -lindorm = [ - { name = "opensearch-py" }, -] -mariadb = [ - { name = "mariadb" }, -] -memorydb = [ - { name = "memorydb" }, -] -mongodb = [ - { name = "pymongo" }, -] -oceanbase = [ - { name = "mysql-connector-python" }, -] -opensearch = [ - { name = "opensearch-py" }, -] -pgvecto-rs = [ - { name = "pgvecto-rs", extra = ["psycopg3"] }, -] -pgvector = [ - { name = "pgvector" }, - { name = "psycopg" }, - { name = "psycopg-binary" }, -] -pinecone = [ - { name = "pinecone" }, -] -qdrant = [ - { name = "qdrant-client" }, -] -redis = [ - { name = "redis" }, -] -restful = [ - { name = "flask" }, -] -test = [ - { name = "black" }, - { name = "pytest" }, - { name = "ruff" }, -] -tidb = [ - { name = "pymysql" }, -] -turbopuffer = [ - { name = "turbopuffer" }, -] -vespa = [ - { name = "pyvespa" }, -] -weaviate = [ - { name = "weaviate-client" }, -] -zvec = [ - { name = "zvec" }, -] - -[package.metadata] -requires-dist = [ - { name = "alibabacloud-ha3engine-vector", marker = "extra == 'aliyun-opensearch'" }, - { name = "alibabacloud-ha3engine-vector", marker = "extra == 'all'" }, - { name = "black", marker = "extra == 'test'" }, - { name = "chromadb", marker = "extra == 'all'" }, - { name = "chromadb", marker = "extra == 'chromadb'" }, - { name = "click" }, - { name = "clickhouse-connect", marker = "extra == 'all'" }, - { name = "clickhouse-connect", marker = "extra == 'clickhouse'" }, - { name = "doris-vector-search", marker = "extra == 'doris'" }, - { name = "elasticsearch", marker = "extra == 'all'" }, - { name = "elasticsearch", marker = "extra == 'elastic'" }, - { name = "endee", marker = "extra == 'all'", specifier = "==0.1.10" }, - { name = "endee", marker = "extra == 'endee'", specifier = "==0.1.10" }, - { name = "environs" }, - { name = "flask", marker = "extra == 'restful'" }, - { name = "grpcio", marker = "extra == 'all'", specifier = "==1.53.0" }, - { name = "grpcio-tools", marker = "extra == 'all'", specifier = "==1.53.0" }, - { name = "hdrhistogram", specifier = ">=0.10.1" }, - { name = "httpx", marker = "extra == 'antfly'" }, - { name = "lancedb", marker = "extra == 'all'" }, - { name = "lancedb", marker = "extra == 'lancedb'" }, - { name = "mariadb", marker = "extra == 'all'" }, - { name = "mariadb", marker = "extra == 'mariadb'" }, - { name = "memorydb", marker = "extra == 'all'" }, - { name = "memorydb", marker = "extra == 'memorydb'" }, - { name = "mysql-connector-python", marker = "extra == 'alisql'" }, - { name = "mysql-connector-python", marker = "extra == 'all'" }, - { name = "mysql-connector-python", marker = "extra == 'oceanbase'" }, - { name = "opensearch-dsl", marker = "extra == 'all'" }, - { name = "opensearch-py", marker = "extra == 'all'" }, - { name = "opensearch-py", marker = "extra == 'lindorm'" }, - { name = "opensearch-py", marker = "extra == 'opensearch'" }, - { name = "oss2" }, - { name = "pgvecto-rs", extras = ["psycopg3"], marker = "extra == 'all'", specifier = ">=0.2.2" }, - { name = "pgvecto-rs", extras = ["psycopg3"], marker = "extra == 'pgvecto-rs'", specifier = ">=0.2.2" }, - { name = "pgvector", marker = "extra == 'all'" }, - { name = "pgvector", marker = "extra == 'cockroachdb'" }, - { name = "pgvector", marker = "extra == 'pgvector'" }, - { name = "pinecone", marker = "extra == 'all'" }, - { name = "pinecone", marker = "extra == 'pinecone'" }, - { name = "plotly" }, - { name = "polars" }, - { name = "psutil" }, - { name = "psycopg", marker = "extra == 'all'" }, - { name = "psycopg", marker = "extra == 'pgvector'" }, - { name = "psycopg", extras = ["binary", "pool"], marker = "extra == 'cockroachdb'" }, - { name = "psycopg-binary", marker = "extra == 'all'" }, - { name = "psycopg-binary", marker = "extra == 'pgvector'" }, - { name = "pydantic", specifier = "<2" }, - { name = "pymilvus" }, - { name = "pymongo", marker = "extra == 'mongodb'" }, - { name = "pymysql", marker = "extra == 'all'" }, - { name = "pymysql", marker = "extra == 'tidb'" }, - { name = "pytest", marker = "extra == 'test'" }, - { name = "pytz" }, - { name = "pyvespa", marker = "extra == 'all'" }, - { name = "pyvespa", marker = "extra == 'vespa'" }, - { name = "qdrant-client", marker = "extra == 'all'" }, - { name = "qdrant-client", marker = "extra == 'qdrant'" }, - { name = "redis", marker = "extra == 'all'" }, - { name = "redis", marker = "extra == 'redis'" }, - { name = "ruff", marker = "extra == 'test'" }, - { name = "s3fs" }, - { name = "scikit-learn" }, - { name = "sqlalchemy", marker = "extra == 'all'" }, - { name = "streamlit", specifier = "!=1.34.0,<1.44" }, - { name = "streamlit-autorefresh" }, - { name = "streamlit-extras" }, - { name = "tqdm" }, - { name = "turbopuffer", marker = "extra == 'turbopuffer'" }, - { name = "turbopuffer", extras = ["fast"], marker = "extra == 'all'" }, - { name = "ujson" }, - { name = "weaviate-client", marker = "extra == 'all'" }, - { name = "weaviate-client", marker = "extra == 'weaviate'" }, - { name = "zvec", marker = "extra == 'all'" }, - { name = "zvec", marker = "extra == 'zvec'" }, -] -provides-extras = ["test", "restful", "all", "qdrant", "pinecone", "weaviate", "elastic", "pgvector", "pgvecto-rs", "redis", "memorydb", "chromadb", "opensearch", "aliyun-opensearch", "mongodb", "mariadb", "tidb", "cockroachdb", "clickhouse", "vespa", "lancedb", "oceanbase", "alisql", "doris", "turbopuffer", "zvec", "endee", "lindorm", "antfly"] - -[[package]] -name = "watchdog" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, -] - -[[package]] -name = "watchfiles" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", size = 406529, upload-time = "2025-10-14T15:04:32.899Z" }, - { url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", size = 394384, upload-time = "2025-10-14T15:04:33.761Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", size = 448789, upload-time = "2025-10-14T15:04:34.679Z" }, - { url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", size = 460521, upload-time = "2025-10-14T15:04:35.963Z" }, - { url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", size = 488722, upload-time = "2025-10-14T15:04:37.091Z" }, - { url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", size = 596088, upload-time = "2025-10-14T15:04:38.39Z" }, - { url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", size = 472923, upload-time = "2025-10-14T15:04:39.666Z" }, - { url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", size = 456080, upload-time = "2025-10-14T15:04:40.643Z" }, - { url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", size = 629432, upload-time = "2025-10-14T15:04:41.789Z" }, - { url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", size = 623046, upload-time = "2025-10-14T15:04:42.718Z" }, - { url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", size = 271473, upload-time = "2025-10-14T15:04:43.624Z" }, - { url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", size = 287598, upload-time = "2025-10-14T15:04:44.516Z" }, - { url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", size = 277210, upload-time = "2025-10-14T15:04:45.883Z" }, - { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, - { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, - { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, - { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, - { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, - { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, - { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, - { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, - { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, - { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, - { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, - { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, - { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, - { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, - { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, - { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, - { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, - { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, - { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, - { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, - { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, - { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, - { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, - { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, - { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, - { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, - { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, - { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", size = 408250, upload-time = "2025-10-14T15:06:10.264Z" }, - { url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", size = 396117, upload-time = "2025-10-14T15:06:11.28Z" }, - { url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", size = 450493, upload-time = "2025-10-14T15:06:12.321Z" }, - { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" }, -] - -[[package]] -name = "weaviate-client" -version = "3.26.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "authlib" }, - { name = "requests" }, - { name = "validators" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f8/2e/9588bae34c1d67d05ccc07d74a4f5d73cce342b916f79ab3a9114c6607bb/weaviate_client-3.26.7.tar.gz", hash = "sha256:ea538437800abc6edba21acf213accaf8a82065584ee8b914bae4a4ad4ef6b70", size = 210480, upload-time = "2024-08-15T13:27:02.431Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/95/fb326052bc1d73cb3c19fcfaf6ebb477f896af68de07eaa1337e27ee57fa/weaviate_client-3.26.7-py3-none-any.whl", hash = "sha256:48b8d4b71df881b4e5e15964d7ac339434338ccee73779e3af7eab698a92083b", size = 120051, upload-time = "2024-08-15T13:27:00.212Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/db/de907251b4ff46ae804ad0409809504153b3f30984daf82a1d84a9875830/websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8", size = 177340, upload-time = "2026-01-10T09:22:34.539Z" }, - { url = "https://files.pythonhosted.org/packages/f3/fa/abe89019d8d8815c8781e90d697dec52523fb8ebe308bf11664e8de1877e/websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad", size = 175022, upload-time = "2026-01-10T09:22:36.332Z" }, - { url = "https://files.pythonhosted.org/packages/58/5d/88ea17ed1ded2079358b40d31d48abe90a73c9e5819dbcde1606e991e2ad/websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d", size = 175319, upload-time = "2026-01-10T09:22:37.602Z" }, - { url = "https://files.pythonhosted.org/packages/d2/ae/0ee92b33087a33632f37a635e11e1d99d429d3d323329675a6022312aac2/websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe", size = 184631, upload-time = "2026-01-10T09:22:38.789Z" }, - { url = "https://files.pythonhosted.org/packages/c8/c5/27178df583b6c5b31b29f526ba2da5e2f864ecc79c99dae630a85d68c304/websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b", size = 185870, upload-time = "2026-01-10T09:22:39.893Z" }, - { url = "https://files.pythonhosted.org/packages/87/05/536652aa84ddc1c018dbb7e2c4cbcd0db884580bf8e95aece7593fde526f/websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5", size = 185361, upload-time = "2026-01-10T09:22:41.016Z" }, - { url = "https://files.pythonhosted.org/packages/6d/e2/d5332c90da12b1e01f06fb1b85c50cfc489783076547415bf9f0a659ec19/websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64", size = 184615, upload-time = "2026-01-10T09:22:42.442Z" }, - { url = "https://files.pythonhosted.org/packages/77/fb/d3f9576691cae9253b51555f841bc6600bf0a983a461c79500ace5a5b364/websockets-16.0-cp311-cp311-win32.whl", hash = "sha256:5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6", size = 178246, upload-time = "2026-01-10T09:22:43.654Z" }, - { url = "https://files.pythonhosted.org/packages/54/67/eaff76b3dbaf18dcddabc3b8c1dba50b483761cccff67793897945b37408/websockets-16.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac", size = 178684, upload-time = "2026-01-10T09:22:44.941Z" }, - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/72/07/c98a68571dcf256e74f1f816b8cc5eae6eb2d3d5cfa44d37f801619d9166/websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d", size = 174947, upload-time = "2026-01-10T09:23:36.166Z" }, - { url = "https://files.pythonhosted.org/packages/7e/52/93e166a81e0305b33fe416338be92ae863563fe7bce446b0f687b9df5aea/websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03", size = 175260, upload-time = "2026-01-10T09:23:37.409Z" }, - { url = "https://files.pythonhosted.org/packages/56/0c/2dbf513bafd24889d33de2ff0368190a0e69f37bcfa19009ef819fe4d507/websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da", size = 176071, upload-time = "2026-01-10T09:23:39.158Z" }, - { url = "https://files.pythonhosted.org/packages/a5/8f/aea9c71cc92bf9b6cc0f7f70df8f0b420636b6c96ef4feee1e16f80f75dd/websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c", size = 176968, upload-time = "2026-01-10T09:23:41.031Z" }, - { url = "https://files.pythonhosted.org/packages/9a/3f/f70e03f40ffc9a30d817eef7da1be72ee4956ba8d7255c399a01b135902a/websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", size = 178735, upload-time = "2026-01-10T09:23:42.259Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] - -[[package]] -name = "werkzeug" -version = "3.1.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" }, -] - -[[package]] -name = "wrapt" -version = "2.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/81/60c4471fce95afa5922ca09b88a25f03c93343f759aae0f31fb4412a85c7/wrapt-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb", size = 60666, upload-time = "2026-03-06T02:52:58.934Z" }, - { url = "https://files.pythonhosted.org/packages/6b/be/80e80e39e7cb90b006a0eaf11c73ac3a62bbfb3068469aec15cc0bc795de/wrapt-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d", size = 61601, upload-time = "2026-03-06T02:53:00.487Z" }, - { url = "https://files.pythonhosted.org/packages/b0/be/d7c88cd9293c859fc74b232abdc65a229bb953997995d6912fc85af18323/wrapt-2.1.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894", size = 114057, upload-time = "2026-03-06T02:52:44.08Z" }, - { url = "https://files.pythonhosted.org/packages/ea/25/36c04602831a4d685d45a93b3abea61eca7fe35dab6c842d6f5d570ef94a/wrapt-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842", size = 116099, upload-time = "2026-03-06T02:54:56.74Z" }, - { url = "https://files.pythonhosted.org/packages/5c/4e/98a6eb417ef551dc277bec1253d5246b25003cf36fdf3913b65cb7657a56/wrapt-2.1.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8", size = 112457, upload-time = "2026-03-06T02:53:52.842Z" }, - { url = "https://files.pythonhosted.org/packages/cb/a6/a6f7186a5297cad8ec53fd7578533b28f795fdf5372368c74bd7e6e9841c/wrapt-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6", size = 115351, upload-time = "2026-03-06T02:53:32.684Z" }, - { url = "https://files.pythonhosted.org/packages/97/6f/06e66189e721dbebd5cf20e138acc4d1150288ce118462f2fcbff92d38db/wrapt-2.1.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9", size = 111748, upload-time = "2026-03-06T02:53:08.455Z" }, - { url = "https://files.pythonhosted.org/packages/ef/43/4808b86f499a51370fbdbdfa6cb91e9b9169e762716456471b619fca7a70/wrapt-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15", size = 113783, upload-time = "2026-03-06T02:53:02.02Z" }, - { url = "https://files.pythonhosted.org/packages/91/2c/a3f28b8fa7ac2cefa01cfcaca3471f9b0460608d012b693998cd61ef43df/wrapt-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b", size = 57977, upload-time = "2026-03-06T02:53:27.844Z" }, - { url = "https://files.pythonhosted.org/packages/3f/c3/2b1c7bd07a27b1db885a2fab469b707bdd35bddf30a113b4917a7e2139d2/wrapt-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1", size = 60336, upload-time = "2026-03-06T02:54:28.104Z" }, - { url = "https://files.pythonhosted.org/packages/ec/5c/76ece7b401b088daa6503d6264dd80f9a727df3e6042802de9a223084ea2/wrapt-2.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a", size = 58756, upload-time = "2026-03-06T02:53:16.319Z" }, - { url = "https://files.pythonhosted.org/packages/4c/b6/1db817582c49c7fcbb7df6809d0f515af29d7c2fbf57eb44c36e98fb1492/wrapt-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9", size = 61255, upload-time = "2026-03-06T02:52:45.663Z" }, - { url = "https://files.pythonhosted.org/packages/a2/16/9b02a6b99c09227c93cd4b73acc3678114154ec38da53043c0ddc1fba0dc/wrapt-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748", size = 61848, upload-time = "2026-03-06T02:53:48.728Z" }, - { url = "https://files.pythonhosted.org/packages/af/aa/ead46a88f9ec3a432a4832dfedb84092fc35af2d0ba40cd04aea3889f247/wrapt-2.1.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e", size = 121433, upload-time = "2026-03-06T02:54:40.328Z" }, - { url = "https://files.pythonhosted.org/packages/3a/9f/742c7c7cdf58b59085a1ee4b6c37b013f66ac33673a7ef4aaed5e992bc33/wrapt-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8", size = 123013, upload-time = "2026-03-06T02:53:26.58Z" }, - { url = "https://files.pythonhosted.org/packages/e8/44/2c3dd45d53236b7ed7c646fcf212251dc19e48e599debd3926b52310fafb/wrapt-2.1.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c", size = 117326, upload-time = "2026-03-06T02:53:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/74/e2/b17d66abc26bd96f89dec0ecd0ef03da4a1286e6ff793839ec431b9fae57/wrapt-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c", size = 121444, upload-time = "2026-03-06T02:54:09.5Z" }, - { url = "https://files.pythonhosted.org/packages/3c/62/e2977843fdf9f03daf1586a0ff49060b1b2fc7ff85a7ea82b6217c1ae36e/wrapt-2.1.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1", size = 116237, upload-time = "2026-03-06T02:54:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/88/dd/27fc67914e68d740bce512f11734aec08696e6b17641fef8867c00c949fc/wrapt-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2", size = 120563, upload-time = "2026-03-06T02:53:20.412Z" }, - { url = "https://files.pythonhosted.org/packages/ec/9f/b750b3692ed2ef4705cb305bd68858e73010492b80e43d2a4faa5573cbe7/wrapt-2.1.2-cp312-cp312-win32.whl", hash = "sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0", size = 58198, upload-time = "2026-03-06T02:53:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/8e/b2/feecfe29f28483d888d76a48f03c4c4d8afea944dbee2b0cd3380f9df032/wrapt-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63", size = 60441, upload-time = "2026-03-06T02:52:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/44/e1/e328f605d6e208547ea9fd120804fcdec68536ac748987a68c47c606eea8/wrapt-2.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf", size = 58836, upload-time = "2026-03-06T02:53:22.053Z" }, - { url = "https://files.pythonhosted.org/packages/4c/7a/d936840735c828b38d26a854e85d5338894cda544cb7a85a9d5b8b9c4df7/wrapt-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b", size = 61259, upload-time = "2026-03-06T02:53:41.922Z" }, - { url = "https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e", size = 61851, upload-time = "2026-03-06T02:52:48.672Z" }, - { url = "https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb", size = 121446, upload-time = "2026-03-06T02:54:14.013Z" }, - { url = "https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca", size = 123056, upload-time = "2026-03-06T02:54:10.829Z" }, - { url = "https://files.pythonhosted.org/packages/93/b9/ff205f391cb708f67f41ea148545f2b53ff543a7ac293b30d178af4d2271/wrapt-2.1.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267", size = 117359, upload-time = "2026-03-06T02:53:03.623Z" }, - { url = "https://files.pythonhosted.org/packages/1f/3d/1ea04d7747825119c3c9a5e0874a40b33594ada92e5649347c457d982805/wrapt-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f", size = 121479, upload-time = "2026-03-06T02:53:45.844Z" }, - { url = "https://files.pythonhosted.org/packages/78/cc/ee3a011920c7a023b25e8df26f306b2484a531ab84ca5c96260a73de76c0/wrapt-2.1.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8", size = 116271, upload-time = "2026-03-06T02:54:46.356Z" }, - { url = "https://files.pythonhosted.org/packages/98/fd/e5ff7ded41b76d802cf1191288473e850d24ba2e39a6ec540f21ae3b57cb/wrapt-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413", size = 120573, upload-time = "2026-03-06T02:52:50.163Z" }, - { url = "https://files.pythonhosted.org/packages/47/c5/242cae3b5b080cd09bacef0591691ba1879739050cc7c801ff35c8886b66/wrapt-2.1.2-cp313-cp313-win32.whl", hash = "sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6", size = 58205, upload-time = "2026-03-06T02:53:47.494Z" }, - { url = "https://files.pythonhosted.org/packages/12/69/c358c61e7a50f290958809b3c61ebe8b3838ea3e070d7aac9814f95a0528/wrapt-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1", size = 60452, upload-time = "2026-03-06T02:53:30.038Z" }, - { url = "https://files.pythonhosted.org/packages/8e/66/c8a6fcfe321295fd8c0ab1bd685b5a01462a9b3aa2f597254462fc2bc975/wrapt-2.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf", size = 58842, upload-time = "2026-03-06T02:52:52.114Z" }, - { url = "https://files.pythonhosted.org/packages/da/55/9c7052c349106e0b3f17ae8db4b23a691a963c334de7f9dbd60f8f74a831/wrapt-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b", size = 63075, upload-time = "2026-03-06T02:53:19.108Z" }, - { url = "https://files.pythonhosted.org/packages/09/a8/ce7b4006f7218248dd71b7b2b732d0710845a0e49213b18faef64811ffef/wrapt-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18", size = 63719, upload-time = "2026-03-06T02:54:33.452Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e5/2ca472e80b9e2b7a17f106bb8f9df1db11e62101652ce210f66935c6af67/wrapt-2.1.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d", size = 152643, upload-time = "2026-03-06T02:52:42.721Z" }, - { url = "https://files.pythonhosted.org/packages/36/42/30f0f2cefca9d9cbf6835f544d825064570203c3e70aa873d8ae12e23791/wrapt-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015", size = 158805, upload-time = "2026-03-06T02:54:25.441Z" }, - { url = "https://files.pythonhosted.org/packages/bb/67/d08672f801f604889dcf58f1a0b424fe3808860ede9e03affc1876b295af/wrapt-2.1.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92", size = 145990, upload-time = "2026-03-06T02:53:57.456Z" }, - { url = "https://files.pythonhosted.org/packages/68/a7/fd371b02e73babec1de6ade596e8cd9691051058cfdadbfd62a5898f3295/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf", size = 155670, upload-time = "2026-03-06T02:54:55.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/9fe0095dfdb621009f40117dcebf41d7396c2c22dca6eac779f4c007b86c/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67", size = 144357, upload-time = "2026-03-06T02:54:24.092Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b6/ec7b4a254abbe4cde9fa15c5d2cca4518f6b07d0f1b77d4ee9655e30280e/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a", size = 150269, upload-time = "2026-03-06T02:53:31.268Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6b/2fabe8ebf148f4ee3c782aae86a795cc68ffe7d432ef550f234025ce0cfa/wrapt-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd", size = 59894, upload-time = "2026-03-06T02:54:15.391Z" }, - { url = "https://files.pythonhosted.org/packages/ca/fb/9ba66fc2dedc936de5f8073c0217b5d4484e966d87723415cc8262c5d9c2/wrapt-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f", size = 63197, upload-time = "2026-03-06T02:54:41.943Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1c/012d7423c95d0e337117723eb8ecf73c622ce15a97847e84cf3f8f26cd7e/wrapt-2.1.2-cp313-cp313t-win_arm64.whl", hash = "sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679", size = 60363, upload-time = "2026-03-06T02:54:48.093Z" }, - { url = "https://files.pythonhosted.org/packages/39/25/e7ea0b417db02bb796182a5316398a75792cd9a22528783d868755e1f669/wrapt-2.1.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9", size = 61418, upload-time = "2026-03-06T02:53:55.706Z" }, - { url = "https://files.pythonhosted.org/packages/ec/0f/fa539e2f6a770249907757eaeb9a5ff4deb41c026f8466c1c6d799088a9b/wrapt-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9", size = 61914, upload-time = "2026-03-06T02:52:53.37Z" }, - { url = "https://files.pythonhosted.org/packages/53/37/02af1867f5b1441aaeda9c82deed061b7cd1372572ddcd717f6df90b5e93/wrapt-2.1.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e", size = 120417, upload-time = "2026-03-06T02:54:30.74Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b7/0138a6238c8ba7476c77cf786a807f871672b37f37a422970342308276e7/wrapt-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c", size = 122797, upload-time = "2026-03-06T02:54:51.539Z" }, - { url = "https://files.pythonhosted.org/packages/e1/ad/819ae558036d6a15b7ed290d5b14e209ca795dd4da9c58e50c067d5927b0/wrapt-2.1.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a", size = 117350, upload-time = "2026-03-06T02:54:37.651Z" }, - { url = "https://files.pythonhosted.org/packages/8b/2d/afc18dc57a4600a6e594f77a9ae09db54f55ba455440a54886694a84c71b/wrapt-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90", size = 121223, upload-time = "2026-03-06T02:54:35.221Z" }, - { url = "https://files.pythonhosted.org/packages/b9/5b/5ec189b22205697bc56eb3b62aed87a1e0423e9c8285d0781c7a83170d15/wrapt-2.1.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586", size = 116287, upload-time = "2026-03-06T02:54:19.654Z" }, - { url = "https://files.pythonhosted.org/packages/f7/2d/f84939a7c9b5e6cdd8a8d0f6a26cabf36a0f7e468b967720e8b0cd2bdf69/wrapt-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19", size = 119593, upload-time = "2026-03-06T02:54:16.697Z" }, - { url = "https://files.pythonhosted.org/packages/0b/fe/ccd22a1263159c4ac811ab9374c061bcb4a702773f6e06e38de5f81a1bdc/wrapt-2.1.2-cp314-cp314-win32.whl", hash = "sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508", size = 58631, upload-time = "2026-03-06T02:53:06.498Z" }, - { url = "https://files.pythonhosted.org/packages/65/0a/6bd83be7bff2e7efaac7b4ac9748da9d75a34634bbbbc8ad077d527146df/wrapt-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04", size = 60875, upload-time = "2026-03-06T02:53:50.252Z" }, - { url = "https://files.pythonhosted.org/packages/6c/c0/0b3056397fe02ff80e5a5d72d627c11eb885d1ca78e71b1a5c1e8c7d45de/wrapt-2.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575", size = 59164, upload-time = "2026-03-06T02:53:59.128Z" }, - { url = "https://files.pythonhosted.org/packages/71/ed/5d89c798741993b2371396eb9d4634f009ff1ad8a6c78d366fe2883ea7a6/wrapt-2.1.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb", size = 63163, upload-time = "2026-03-06T02:52:54.873Z" }, - { url = "https://files.pythonhosted.org/packages/c6/8c/05d277d182bf36b0a13d6bd393ed1dec3468a25b59d01fba2dd70fe4d6ae/wrapt-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22", size = 63723, upload-time = "2026-03-06T02:52:56.374Z" }, - { url = "https://files.pythonhosted.org/packages/f4/27/6c51ec1eff4413c57e72d6106bb8dec6f0c7cdba6503d78f0fa98767bcc9/wrapt-2.1.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596", size = 152652, upload-time = "2026-03-06T02:53:23.79Z" }, - { url = "https://files.pythonhosted.org/packages/db/4c/d7dd662d6963fc7335bfe29d512b02b71cdfa23eeca7ab3ac74a67505deb/wrapt-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044", size = 158807, upload-time = "2026-03-06T02:53:35.742Z" }, - { url = "https://files.pythonhosted.org/packages/b4/4d/1e5eea1a78d539d346765727422976676615814029522c76b87a95f6bcdd/wrapt-2.1.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b", size = 146061, upload-time = "2026-03-06T02:52:57.574Z" }, - { url = "https://files.pythonhosted.org/packages/89/bc/62cabea7695cd12a288023251eeefdcb8465056ddaab6227cb78a2de005b/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf", size = 155667, upload-time = "2026-03-06T02:53:39.422Z" }, - { url = "https://files.pythonhosted.org/packages/e9/99/6f2888cd68588f24df3a76572c69c2de28287acb9e1972bf0c83ce97dbc1/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2", size = 144392, upload-time = "2026-03-06T02:54:22.41Z" }, - { url = "https://files.pythonhosted.org/packages/40/51/1dfc783a6c57971614c48e361a82ca3b6da9055879952587bc99fe1a7171/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3", size = 150296, upload-time = "2026-03-06T02:54:07.848Z" }, - { url = "https://files.pythonhosted.org/packages/6c/38/cbb8b933a0201076c1f64fc42883b0023002bdc14a4964219154e6ff3350/wrapt-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7", size = 60539, upload-time = "2026-03-06T02:54:00.594Z" }, - { url = "https://files.pythonhosted.org/packages/82/dd/e5176e4b241c9f528402cebb238a36785a628179d7d8b71091154b3e4c9e/wrapt-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5", size = 63969, upload-time = "2026-03-06T02:54:39Z" }, - { url = "https://files.pythonhosted.org/packages/5c/99/79f17046cf67e4a95b9987ea129632ba8bcec0bc81f3fb3d19bdb0bd60cd/wrapt-2.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00", size = 60554, upload-time = "2026-03-06T02:53:14.132Z" }, - { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, -] - -[[package]] -name = "yarl" -version = "1.23.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/aa/60da938b8f0997ba3a911263c40d82b6f645a67902a490b46f3355e10fae/yarl-1.23.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99", size = 123641, upload-time = "2026-03-01T22:04:42.841Z" }, - { url = "https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c", size = 86248, upload-time = "2026-03-01T22:04:44.757Z" }, - { url = "https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432", size = 85988, upload-time = "2026-03-01T22:04:46.365Z" }, - { url = "https://files.pythonhosted.org/packages/8c/6c/4a90d59c572e46b270ca132aca66954f1175abd691f74c1ef4c6711828e2/yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a", size = 100566, upload-time = "2026-03-01T22:04:47.639Z" }, - { url = "https://files.pythonhosted.org/packages/49/fb/c438fb5108047e629f6282a371e6e91cf3f97ee087c4fb748a1f32ceef55/yarl-1.23.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05", size = 92079, upload-time = "2026-03-01T22:04:48.925Z" }, - { url = "https://files.pythonhosted.org/packages/d9/13/d269aa1aed3e4f50a5a103f96327210cc5fa5dd2d50882778f13c7a14606/yarl-1.23.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83", size = 108741, upload-time = "2026-03-01T22:04:50.838Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/115b16f22c37ea4437d323e472945bea97301c8ec6089868fa560abab590/yarl-1.23.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c", size = 108099, upload-time = "2026-03-01T22:04:52.499Z" }, - { url = "https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598", size = 102678, upload-time = "2026-03-01T22:04:55.176Z" }, - { url = "https://files.pythonhosted.org/packages/85/59/cd98e556fbb2bf8fab29c1a722f67ad45c5f3447cac798ab85620d1e70af/yarl-1.23.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b", size = 100803, upload-time = "2026-03-01T22:04:56.588Z" }, - { url = "https://files.pythonhosted.org/packages/9e/c0/b39770b56d4a9f0bb5f77e2f1763cd2d75cc2f6c0131e3b4c360348fcd65/yarl-1.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c", size = 100163, upload-time = "2026-03-01T22:04:58.492Z" }, - { url = "https://files.pythonhosted.org/packages/e7/64/6980f99ab00e1f0ff67cb84766c93d595b067eed07439cfccfc8fb28c1a6/yarl-1.23.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788", size = 93859, upload-time = "2026-03-01T22:05:00.268Z" }, - { url = "https://files.pythonhosted.org/packages/38/69/912e6c5e146793e5d4b5fe39ff5b00f4d22463dfd5a162bec565ac757673/yarl-1.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222", size = 108202, upload-time = "2026-03-01T22:05:02.273Z" }, - { url = "https://files.pythonhosted.org/packages/59/97/35ca6767524687ad64e5f5c31ad54bc76d585585a9fcb40f649e7e82ffed/yarl-1.23.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb", size = 99866, upload-time = "2026-03-01T22:05:03.597Z" }, - { url = "https://files.pythonhosted.org/packages/d3/1c/1a3387ee6d73589f6f2a220ae06f2984f6c20b40c734989b0a44f5987308/yarl-1.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc", size = 107852, upload-time = "2026-03-01T22:05:04.986Z" }, - { url = "https://files.pythonhosted.org/packages/a4/b8/35c0750fcd5a3f781058bfd954515dd4b1eab45e218cbb85cf11132215f1/yarl-1.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2", size = 102919, upload-time = "2026-03-01T22:05:06.397Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1c/9a1979aec4a81896d597bcb2177827f2dbee3f5b7cc48b2d0dadb644b41d/yarl-1.23.0-cp311-cp311-win32.whl", hash = "sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5", size = 82602, upload-time = "2026-03-01T22:05:08.444Z" }, - { url = "https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46", size = 87461, upload-time = "2026-03-01T22:05:10.145Z" }, - { url = "https://files.pythonhosted.org/packages/93/95/07e3553fe6f113e6864a20bdc53a78113cda3b9ced8784ee52a52c9f80d8/yarl-1.23.0-cp311-cp311-win_arm64.whl", hash = "sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928", size = 82336, upload-time = "2026-03-01T22:05:11.554Z" }, - { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, - { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, - { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, - { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, - { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, - { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, - { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, - { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, - { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, - { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, - { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, - { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, - { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, - { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, - { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, - { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, - { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, - { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, - { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, - { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, - { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, - { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, - { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, - { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, - { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, - { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, - { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, - { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, - { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, - { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, - { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, - { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, - { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, - { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, - { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, - { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, - { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, - { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, - { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, - { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, - { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, - { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, - { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, - { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, - { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, - { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, - { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, - { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, - { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, - { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, - { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, - { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, - { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, - { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, - { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, - { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, - { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, - { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, - { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, - { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, - { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, - { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, - { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, - { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, - { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, - { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, - { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, - { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, - { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, -] - -[[package]] -name = "zstandard" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" }, - { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" }, - { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" }, - { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" }, - { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" }, - { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" }, - { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" }, - { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" }, - { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" }, - { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" }, - { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" }, - { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" }, - { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" }, - { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" }, - { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" }, - { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" }, - { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" }, - { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" }, - { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" }, - { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" }, - { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" }, - { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" }, - { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" }, - { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" }, - { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" }, - { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" }, - { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" }, - { url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922, upload-time = "2025-09-14T22:17:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276, upload-time = "2025-09-14T22:17:21.429Z" }, - { url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679, upload-time = "2025-09-14T22:17:23.147Z" }, - { url = "https://files.pythonhosted.org/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735, upload-time = "2025-09-14T22:17:26.042Z" }, - { url = "https://files.pythonhosted.org/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440, upload-time = "2025-09-14T22:17:27.366Z" }, - { url = "https://files.pythonhosted.org/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070, upload-time = "2025-09-14T22:17:28.896Z" }, - { url = "https://files.pythonhosted.org/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001, upload-time = "2025-09-14T22:17:31.044Z" }, - { url = "https://files.pythonhosted.org/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120, upload-time = "2025-09-14T22:17:32.711Z" }, - { url = "https://files.pythonhosted.org/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230, upload-time = "2025-09-14T22:17:34.41Z" }, - { url = "https://files.pythonhosted.org/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173, upload-time = "2025-09-14T22:17:36.084Z" }, - { url = "https://files.pythonhosted.org/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736, upload-time = "2025-09-14T22:17:37.891Z" }, - { url = "https://files.pythonhosted.org/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368, upload-time = "2025-09-14T22:17:40.206Z" }, - { url = "https://files.pythonhosted.org/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022, upload-time = "2025-09-14T22:17:41.879Z" }, - { url = "https://files.pythonhosted.org/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889, upload-time = "2025-09-14T22:17:43.577Z" }, - { url = "https://files.pythonhosted.org/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952, upload-time = "2025-09-14T22:17:45.271Z" }, - { url = "https://files.pythonhosted.org/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054, upload-time = "2025-09-14T22:17:47.08Z" }, - { url = "https://files.pythonhosted.org/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113, upload-time = "2025-09-14T22:17:48.893Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936, upload-time = "2025-09-14T22:17:52.658Z" }, - { url = "https://files.pythonhosted.org/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232, upload-time = "2025-09-14T22:17:50.402Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671, upload-time = "2025-09-14T22:17:51.533Z" }, - { url = "https://files.pythonhosted.org/packages/3d/5c/f8923b595b55fe49e30612987ad8bf053aef555c14f05bb659dd5dbe3e8a/zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3", size = 795887, upload-time = "2025-09-14T22:17:54.198Z" }, - { url = "https://files.pythonhosted.org/packages/8d/09/d0a2a14fc3439c5f874042dca72a79c70a532090b7ba0003be73fee37ae2/zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f", size = 640658, upload-time = "2025-09-14T22:17:55.423Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7c/8b6b71b1ddd517f68ffb55e10834388d4f793c49c6b83effaaa05785b0b4/zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c", size = 5379849, upload-time = "2025-09-14T22:17:57.372Z" }, - { url = "https://files.pythonhosted.org/packages/a4/86/a48e56320d0a17189ab7a42645387334fba2200e904ee47fc5a26c1fd8ca/zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439", size = 5058095, upload-time = "2025-09-14T22:17:59.498Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ad/eb659984ee2c0a779f9d06dbfe45e2dc39d99ff40a319895df2d3d9a48e5/zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043", size = 5551751, upload-time = "2025-09-14T22:18:01.618Z" }, - { url = "https://files.pythonhosted.org/packages/61/b3/b637faea43677eb7bd42ab204dfb7053bd5c4582bfe6b1baefa80ac0c47b/zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859", size = 6364818, upload-time = "2025-09-14T22:18:03.769Z" }, - { url = "https://files.pythonhosted.org/packages/31/dc/cc50210e11e465c975462439a492516a73300ab8caa8f5e0902544fd748b/zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0", size = 5560402, upload-time = "2025-09-14T22:18:05.954Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ae/56523ae9c142f0c08efd5e868a6da613ae76614eca1305259c3bf6a0ed43/zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7", size = 4955108, upload-time = "2025-09-14T22:18:07.68Z" }, - { url = "https://files.pythonhosted.org/packages/98/cf/c899f2d6df0840d5e384cf4c4121458c72802e8bda19691f3b16619f51e9/zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2", size = 5269248, upload-time = "2025-09-14T22:18:09.753Z" }, - { url = "https://files.pythonhosted.org/packages/1b/c0/59e912a531d91e1c192d3085fc0f6fb2852753c301a812d856d857ea03c6/zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344", size = 5430330, upload-time = "2025-09-14T22:18:11.966Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/7e31db1240de2df22a58e2ea9a93fc6e38cc29353e660c0272b6735d6669/zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c", size = 5811123, upload-time = "2025-09-14T22:18:13.907Z" }, - { url = "https://files.pythonhosted.org/packages/f6/49/fac46df5ad353d50535e118d6983069df68ca5908d4d65b8c466150a4ff1/zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088", size = 5359591, upload-time = "2025-09-14T22:18:16.465Z" }, - { url = "https://files.pythonhosted.org/packages/c2/38/f249a2050ad1eea0bb364046153942e34abba95dd5520af199aed86fbb49/zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12", size = 444513, upload-time = "2025-09-14T22:18:20.61Z" }, - { url = "https://files.pythonhosted.org/packages/3a/43/241f9615bcf8ba8903b3f0432da069e857fc4fd1783bd26183db53c4804b/zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2", size = 516118, upload-time = "2025-09-14T22:18:17.849Z" }, - { url = "https://files.pythonhosted.org/packages/f0/ef/da163ce2450ed4febf6467d77ccb4cd52c4c30ab45624bad26ca0a27260c/zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d", size = 476940, upload-time = "2025-09-14T22:18:19.088Z" }, -] - -[[package]] -name = "zvec" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/8a/07bfee47f4ad0fe959b91d6db26c88d477a482003246516b123874d99c8b/zvec-0.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:972297f77446f0b938f91a455acc91858bb7d7ddc2536be6b3394447c1787d4e", size = 27185332, upload-time = "2026-04-03T08:49:06.907Z" }, - { url = "https://files.pythonhosted.org/packages/95/ec/38f5787f4b2bc295b06c5ef26d30f4e824eb122420a6f91ddfbd0aef657b/zvec-0.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c2f549743843550734c37435d85ae58ff4b4b4a03219103ba33098363c8dc15e", size = 39058326, upload-time = "2026-04-03T09:08:45.65Z" }, - { url = "https://files.pythonhosted.org/packages/85/45/3bf2f754128d4fa599f520efc5c29b7e29239e071be48ac01380c9d93262/zvec-0.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1277b165502583e2e16c4aceecfc88a5365fbc18d14bb648aff391991be67596", size = 44088079, upload-time = "2026-04-03T09:14:36.92Z" }, - { url = "https://files.pythonhosted.org/packages/05/42/97c4c559ccdd320c03c6b19f1d6691bfd9d5c7495433dfc6b1964944374c/zvec-0.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:a93db72899b06611a3910df14a5e244bfed16444d774ce5f32ffcd2bf0c710da", size = 46997079, upload-time = "2026-04-03T09:02:47.973Z" }, - { url = "https://files.pythonhosted.org/packages/9d/2d/8da245b2ceee956a3d451fc2ea22ac7d9a63a9f650940a5d91def391f34e/zvec-0.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:931045131f21ac1892d0d086cf0b03d5d2873c88ca5b60da57ff6ffc1353104e", size = 27187639, upload-time = "2026-04-03T08:49:09.271Z" }, - { url = "https://files.pythonhosted.org/packages/e8/d6/3c3d0935e7a1b23a677483718795629dc0cfa80dc6596d2b9293bed90762/zvec-0.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d71900105b232ed75ab765cc4df404de94f228183d987ef93dc0e335e1b1e980", size = 39060212, upload-time = "2026-04-03T09:08:48.65Z" }, - { url = "https://files.pythonhosted.org/packages/a3/19/eeaaeeaef010cbf584e2c9b8252d4237118ba133c2798a5c3450fc6fa989/zvec-0.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:afe72d9e4cedbc2e30e48284384f70689098447fadb126b034a789916ea63f23", size = 44091690, upload-time = "2026-04-03T09:14:39.381Z" }, - { url = "https://files.pythonhosted.org/packages/1d/b1/8b3f8b8a0b9551268d7a4af6675f9d95474b9f9b25aac2f81786429c2f1e/zvec-0.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:e8664d3036b016c21541f005fd3287e5b0756585760f7fa28a4328fe221efe41", size = 47001437, upload-time = "2026-04-03T09:02:56.122Z" }, - { url = "https://files.pythonhosted.org/packages/c6/26/c61a1bb6a8a94a4ca80faad204e7ea9e134ec5b0348da0905162d00b4eb3/zvec-0.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97d2dd089503be372c08f65fe2472e6dafe9ecdd96721f5e80a03961c9a8264d", size = 27187610, upload-time = "2026-04-03T08:49:11.6Z" }, - { url = "https://files.pythonhosted.org/packages/b4/7b/d47617aac5856dee1b1e55a842f0b948447fa178828be60d6c5fcb704fa7/zvec-0.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:8c9518c43ed9e661407688995eb35f3b1164d8f4e33ae91951a193239a28b547", size = 39060122, upload-time = "2026-04-03T09:08:51.66Z" }, - { url = "https://files.pythonhosted.org/packages/e8/d8/2499254e25b5d37590806a6cdad8c65851cd561e5809b65fb0e1bbb2a322/zvec-0.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:68577ef61f83ce85edd15ef0f92453a9e95332aeec2db9fcb925eb5e72391dda", size = 44092146, upload-time = "2026-04-03T09:14:41.988Z" }, - { url = "https://files.pythonhosted.org/packages/93/57/42bd185ec976cf580bef70d7fe64ccb59602bb887bb8f6c5c81b18eea3d8/zvec-0.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:9be8f2026ee8d2992ab68fba116317b39c3ab341afa030aeeaa3612bdd4c0e06", size = 47001602, upload-time = "2026-04-03T09:03:01.671Z" }, - { url = "https://files.pythonhosted.org/packages/95/e7/be9dcb896b543924faea884f34d26284aa40c6e1733f2174ea5e282a1582/zvec-0.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dd805222d9f1013d3e949753ce39d18bd572e7b652b0bf308f8fe61df0aa3877", size = 27188469, upload-time = "2026-04-03T08:49:13.922Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4a/6e5ab7f5e91437eefd5f4e0637713896f161b4a7230231cf65b495e1994d/zvec-0.3.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:aca6398e29820f0da343f5fb92bdd7b99090e8ad07d06e6682d6c0532ad9f420", size = 39061668, upload-time = "2026-04-03T09:08:55.03Z" }, - { url = "https://files.pythonhosted.org/packages/14/35/d619ddc85da0eb9502c248af10ccbf391a7080263e4d2f133d8b5f99403f/zvec-0.3.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b57fbd963c9dcca1a5050ce52338cabe8a50fcf0bf19fa78c145e4d470b847f7", size = 44092627, upload-time = "2026-04-03T09:14:46.693Z" }, - { url = "https://files.pythonhosted.org/packages/9f/65/f161006bd6c574de5d73cc426f8038c58790b0fa889002b575268ec393f7/zvec-0.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a60da0a40dc35a58da6c1f6776359b94e433e4675e144ee2a09a2962881bb5ea", size = 48045220, upload-time = "2026-04-03T09:03:07.333Z" }, -] diff --git a/vectordb_bench/__init__.py b/vectordb_bench/__init__.py index 07f77bb02..1491630e0 100644 --- a/vectordb_bench/__init__.py +++ b/vectordb_bench/__init__.py @@ -17,9 +17,10 @@ class config: LOG_FILE = env.str("LOG_FILE", "logs/vectordb_bench.log") DEFAULT_DATASET_URL = env.str("DEFAULT_DATASET_URL", AWS_S3_URL) - DATASET_SOURCE = env.str("DATASET_SOURCE", "S3") # Options "S3" or "AliyunOSS" + DATASET_SOURCE = env.str("DATASET_SOURCE", "S3") # Options "S3", "AliyunOSS", or "IR_DATASETS" DATASET_LOCAL_DIR = env.path("DATASET_LOCAL_DIR", "/tmp/vectordb_bench/dataset") NUM_PER_BATCH = env.int("NUM_PER_BATCH", 100) + LOAD_CONCURRENCY = env.int("LOAD_CONCURRENCY", 0) # 0 = cpu_count TIME_PER_BATCH = 1 # 1s. for streaming insertion. MAX_INSERT_RETRY = 5 MAX_SEARCH_RETRY = 5 @@ -34,6 +35,9 @@ class config: CONCURRENCY_DURATION = 30 CONCURRENCY_TIMEOUT = 3600 + SERIAL_COOLDOWN = 0 + CLOUD_INSERT_READINESS_TIMEOUT = env.float("CLOUD_INSERT_READINESS_TIMEOUT", None) + CLOUD_INSERT_READINESS_POLL_INTERVAL = env.float("CLOUD_INSERT_READINESS_POLL_INTERVAL", 5.0) RESULTS_LOCAL_DIR = env.path( "RESULTS_LOCAL_DIR", diff --git a/vectordb_bench/backend/assembler.py b/vectordb_bench/backend/assembler.py index 3268cde35..fe7f0ddc6 100644 --- a/vectordb_bench/backend/assembler.py +++ b/vectordb_bench/backend/assembler.py @@ -6,6 +6,7 @@ from vectordb_bench.models import TaskConfig from .cases import CaseLabel +from .dataset import FtsDatasetManager from .task_runner import CaseRunner, RunningStatus, TaskRunner log = logging.getLogger(__name__) @@ -24,7 +25,18 @@ def assemble(cls, run_id: str, task: TaskConfig, source: DatasetSource) -> CaseR c_cls = task.case_config.case_id.case_cls c = c_cls(task.case_config.custom_case) - if type(task.db_case_config) is not EmptyDBCaseConfig: + if c.label == CaseLabel.FullTextSearchPerformance and not task.db.init_cls.supports_full_text_search(): + msg = f"{task.db.value} does not support full-text search" + raise ValueError(msg) + + # Auto-select data source based on dataset type + actual_source = DatasetSource.IR_DATASETS if isinstance(c.dataset, FtsDatasetManager) else source + + if ( + type(task.db_case_config) is not EmptyDBCaseConfig + and not isinstance(c.dataset, FtsDatasetManager) + and hasattr(c.dataset.data, "metric_type") + ): task.db_case_config.metric_type = c.dataset.data.metric_type return CaseRunner( @@ -32,7 +44,7 @@ def assemble(cls, run_id: str, task: TaskConfig, source: DatasetSource) -> CaseR config=task, ca=c, status=RunningStatus.PENDING, - dataset_source=source, + dataset_source=actual_source, ) @classmethod @@ -48,10 +60,15 @@ def assemble_all( load_runners = [r for r in runners if r.ca.label == CaseLabel.Load] perf_runners = [r for r in runners if r.ca.label == CaseLabel.Performance] streaming_runners = [r for r in runners if r.ca.label == CaseLabel.Streaming] + cloud_insert_runners = [r for r in runners if r.ca.label == CaseLabel.CloudInsert] + cloud_cold_latency_runners = [r for r in runners if r.ca.label == CaseLabel.CloudColdLatency] + fts_runners = [r for r in runners if r.ca.label == CaseLabel.FullTextSearchPerformance] + + search_filter_runners = [*perf_runners, *cloud_cold_latency_runners] # group by db db2runner: dict[DB, list[CaseRunner]] = {} - for r in perf_runners: + for r in search_filter_runners: db = r.config.db if db not in db2runner: db2runner[db] = [] @@ -71,8 +88,10 @@ def assemble_all( all_runners = [] all_runners.extend(load_runners) all_runners.extend(streaming_runners) + all_runners.extend(cloud_insert_runners) for v in db2runner.values(): all_runners.extend(v) + all_runners.extend(fts_runners) return TaskRunner( run_id=run_id, diff --git a/vectordb_bench/backend/cases.py b/vectordb_bench/backend/cases.py index edfcdea19..24eaf768e 100644 --- a/vectordb_bench/backend/cases.py +++ b/vectordb_bench/backend/cases.py @@ -5,13 +5,32 @@ from vectordb_bench import config from vectordb_bench.backend.clients.api import MetricType from vectordb_bench.backend.filter import Filter, FilterOp, IntFilter, LabelFilter, NewIntFilter, NonFilter, non_filter +from vectordb_bench.backend.payload import PayloadProfile from vectordb_bench.base import BaseModel from vectordb_bench.frontend.components.custom.getCustomConfig import CustomDatasetConfig -from .dataset import CustomDataset, Dataset, DatasetManager, DatasetWithSizeType +from .dataset import ( + CustomDataset, + Dataset, + DatasetManager, + DatasetWithSizeType, + FtsDatasetManager, + FtsDatasetWithSizeType, +) log = logging.getLogger(__name__) +FTS_FILTER_ID_FIELD = "filter_id" +FTS_FILTER_RATES = (0.5, 0.75, 0.9, 0.95, 0.99) + + +def _format_filter_rate(filter_rate: float) -> str: + return f"{filter_rate * 100:g}%" + + +def _is_supported_fts_filter_rate(filter_rate: float) -> bool: + return any(abs(filter_rate - supported_rate) < 1e-9 for supported_rate in FTS_FILTER_RATES) + class CaseType(Enum): """ @@ -56,6 +75,11 @@ class CaseType(Enum): LabelFilterPerformanceCase = 300 NewIntFilterPerformanceCase = 400 + CloudPayloadSearchCase = 500 + FTSBm25Performance = 503 + CloudInsertCase = 600 + CloudColdLatencyCase = 700 + CloudMultiTenantSearchCase = 800 def case_cls(self, custom_configs: dict | None = None) -> type["Case"]: if custom_configs is None: @@ -79,6 +103,9 @@ class CaseLabel(Enum): Load = auto() Performance = auto() Streaming = auto() + CloudInsert = auto() + CloudColdLatency = auto() + FullTextSearchPerformance = auto() class Case(BaseModel): @@ -102,14 +129,24 @@ class Case(BaseModel): optimize_timeout: float | int | None = None filter_rate: float | None = None + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY @property def filters(self) -> Filter: return non_filter + def estimated_payload_bytes_per_query(self, k: int | None) -> int: + if k is None: + k = config.K_DEFAULT + return self.payload_profile.estimated_bytes_per_query(k=k, dim=self.dataset.data.dim) + + @property + def is_multitenant(self) -> bool: + return False + @property def with_scalar_labels(self) -> bool: - return self.filters.type == FilterOp.StrEqual + return self.filters.type == FilterOp.StrEqual or self.payload_profile == PayloadProfile.SCALAR_LABEL def check_scalar_labels(self) -> None: if self.with_scalar_labels and not self.dataset.data.with_scalar_labels: @@ -594,6 +631,259 @@ def filters(self) -> Filter: return NewIntFilter(filter_rate=self.filter_rate, int_field=int_field, int_value=int_value) +class CloudPayloadSearchCase(PerformanceCase): + case_id: CaseType = CaseType.CloudPayloadSearchCase + dataset_with_size_type: DatasetWithSizeType | None = None + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY + filter_rate: float | None = None + label_percentage: float | None = None + + def __init__( + self, + dataset_with_size_type: DatasetWithSizeType | str | None = None, + payload_profile: PayloadProfile | str = PayloadProfile.IDS_ONLY, + filter_rate: float | None = None, + label_percentage: float | None = None, + **kwargs, + ): + if filter_rate is not None and label_percentage is not None: + msg = "CloudPayloadSearchCase supports only one filter type per run" + raise ValueError(msg) + if dataset_with_size_type is not None and not isinstance(dataset_with_size_type, DatasetWithSizeType): + dataset_with_size_type = DatasetWithSizeType(dataset_with_size_type) + if not isinstance(payload_profile, PayloadProfile): + payload_profile = PayloadProfile(payload_profile) + + if dataset_with_size_type is None: + dataset = Dataset.LAION.manager(100_000_000) + load_timeout = config.LOAD_TIMEOUT_768D_100M + optimize_timeout = config.OPTIMIZE_TIMEOUT_768D_100M + dataset_name = "LAION 100M (768dim)" + else: + dataset = dataset_with_size_type.get_manager() + load_timeout = dataset_with_size_type.get_load_timeout() + optimize_timeout = dataset_with_size_type.get_optimize_timeout() + dataset_name = dataset_with_size_type.value + + name = f"Cloud Payload Search - {payload_profile.value} - {dataset_name}" + description = ( + "Cloud leaderboard search envelope case with explicit response payload profile. " + f"Payload profile: {payload_profile.value}; dataset: {dataset_name}." + ) + super().__init__( + name=name, + description=description, + dataset=dataset, + load_timeout=load_timeout, + optimize_timeout=optimize_timeout, + dataset_with_size_type=dataset_with_size_type, + payload_profile=payload_profile, + filter_rate=filter_rate, + label_percentage=label_percentage, + **kwargs, + ) + + @property + def filters(self) -> Filter: + if self.label_percentage is not None: + return LabelFilter(label_percentage=self.label_percentage) + if self.filter_rate is None: + return non_filter + int_field = self.dataset.data.train_id_field + int_value = int(self.dataset.data.size * self.filter_rate) + return NewIntFilter(filter_rate=self.filter_rate, int_field=int_field, int_value=int_value) + + +class CloudColdLatencyCase(Case): + case_id: CaseType = CaseType.CloudColdLatencyCase + label: CaseLabel = CaseLabel.CloudColdLatency + dataset_with_size_type: DatasetWithSizeType | None = None + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY + filter_rate: float | None = None + label_percentage: float | None = None + query_count: int = 1000 + + def __init__( + self, + dataset_with_size_type: DatasetWithSizeType | str | None = None, + payload_profile: PayloadProfile | str = PayloadProfile.IDS_ONLY, + filter_rate: float | None = None, + label_percentage: float | None = None, + query_count: int = 1000, + **kwargs, + ): + if filter_rate is not None and label_percentage is not None: + msg = "CloudColdLatencyCase supports only one filter type per run" + raise ValueError(msg) + if query_count <= 0: + msg = "query_count must be positive" + raise ValueError(msg) + if dataset_with_size_type is not None and not isinstance(dataset_with_size_type, DatasetWithSizeType): + dataset_with_size_type = DatasetWithSizeType(dataset_with_size_type) + if not isinstance(payload_profile, PayloadProfile): + payload_profile = PayloadProfile(payload_profile) + + if dataset_with_size_type is None: + dataset = Dataset.LAION.manager(100_000_000) + load_timeout = config.LOAD_TIMEOUT_768D_100M + optimize_timeout = config.OPTIMIZE_TIMEOUT_768D_100M + dataset_name = "LAION 100M (768dim)" + else: + dataset = dataset_with_size_type.get_manager() + load_timeout = dataset_with_size_type.get_load_timeout() + optimize_timeout = dataset_with_size_type.get_optimize_timeout() + dataset_name = dataset_with_size_type.value + + name = f"Cloud Cold Latency - {payload_profile.value} - {dataset_name}" + description = ( + "Cloud leaderboard cold/warm serial latency case with explicit response payload profile. " + f"Payload profile: {payload_profile.value}; dataset: {dataset_name}; query count: {query_count}." + ) + super().__init__( + name=name, + description=description, + dataset=dataset, + load_timeout=load_timeout, + optimize_timeout=optimize_timeout, + dataset_with_size_type=dataset_with_size_type, + payload_profile=payload_profile, + filter_rate=filter_rate, + label_percentage=label_percentage, + query_count=query_count, + **kwargs, + ) + + @property + def filters(self) -> Filter: + if self.label_percentage is not None: + return LabelFilter(label_percentage=self.label_percentage) + if self.filter_rate is None: + return non_filter + int_field = self.dataset.data.train_id_field + int_value = int(self.dataset.data.size * self.filter_rate) + return NewIntFilter(filter_rate=self.filter_rate, int_field=int_field, int_value=int_value) + + +class CloudInsertCase(Case): + case_id: CaseType = CaseType.CloudInsertCase + label: CaseLabel = CaseLabel.CloudInsert + batch_size: int + duration: float | None = None + readiness_timeout: float | None = config.CLOUD_INSERT_READINESS_TIMEOUT + readiness_poll_interval: float = config.CLOUD_INSERT_READINESS_POLL_INTERVAL + dataset_with_size_type: DatasetWithSizeType | None = None + + def __init__( + self, + batch_size: int, + duration: float | None = None, + readiness_timeout: float | None = config.CLOUD_INSERT_READINESS_TIMEOUT, + readiness_poll_interval: float = config.CLOUD_INSERT_READINESS_POLL_INTERVAL, + dataset_with_size_type: DatasetWithSizeType | str | None = None, + **kwargs, + ): + if dataset_with_size_type is not None and not isinstance(dataset_with_size_type, DatasetWithSizeType): + dataset_with_size_type = DatasetWithSizeType(dataset_with_size_type) + dataset = ( + Dataset.LAION.manager(100_000_000) + if dataset_with_size_type is None + else dataset_with_size_type.get_manager() + ) + super().__init__( + name=f"Cloud Insert - batch {batch_size}", + description="Cloud leaderboard insert-only case with readiness polling.", + dataset=dataset, + batch_size=batch_size, + duration=duration, + readiness_timeout=readiness_timeout, + readiness_poll_interval=readiness_poll_interval, + dataset_with_size_type=dataset_with_size_type, + **kwargs, + ) + + +class CloudMultiTenantSearchCase(PerformanceCase): + case_id: CaseType = CaseType.CloudMultiTenantSearchCase + dataset_with_size_type: DatasetWithSizeType = DatasetWithSizeType.CohereLarge + tenant_count: int = 1000 + tenant_prefix: str = "tenant_" + tenant_id_width: int = 4 + tenant_distribution: str = "uniform_by_id_mod" + measure_recall: bool = False + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY + filter_rate: float | None = None + label_percentage: float | None = None + + def __init__( + self, + dataset_with_size_type: DatasetWithSizeType | str = DatasetWithSizeType.CohereLarge, + tenant_count: int = 1000, + tenant_prefix: str = "tenant_", + tenant_id_width: int = 4, + payload_profile: PayloadProfile | str = PayloadProfile.IDS_ONLY, + filter_rate: float | None = None, + label_percentage: float | None = None, + **kwargs, + ): + if filter_rate is not None and label_percentage is not None: + msg = "CloudMultiTenantSearchCase supports only one filter type per run" + raise ValueError(msg) + if not isinstance(dataset_with_size_type, DatasetWithSizeType): + dataset_with_size_type = DatasetWithSizeType(dataset_with_size_type) + if not isinstance(payload_profile, PayloadProfile): + payload_profile = PayloadProfile(payload_profile) + if tenant_count <= 0: + msg = "tenant_count must be greater than 0" + raise ValueError(msg) + if tenant_id_width <= 0: + msg = "tenant_id_width must be greater than 0" + raise ValueError(msg) + + dataset = dataset_with_size_type.get_manager() + super().__init__( + name=f"Cloud Multi-Tenant Search - {dataset_with_size_type.value}, {tenant_count} tenants", + description=( + "Multi-tenant QPS/latency benchmark with deterministic tenant routing " + f"({dataset_with_size_type.value}, {tenant_count} tenants)." + ), + dataset=dataset, + load_timeout=dataset_with_size_type.get_load_timeout(), + optimize_timeout=dataset_with_size_type.get_optimize_timeout(), + dataset_with_size_type=dataset_with_size_type, + tenant_count=tenant_count, + tenant_prefix=tenant_prefix, + tenant_id_width=tenant_id_width, + payload_profile=payload_profile, + filter_rate=filter_rate, + label_percentage=label_percentage, + **kwargs, + ) + + @property + def is_multitenant(self) -> bool: + return True + + def tenant_for_id(self, row_id: int) -> str: + tenant_id = int(row_id) % self.tenant_count + return f"{self.tenant_prefix}{tenant_id:0{self.tenant_id_width}d}" + + def tenant_labels_for_ids(self, row_ids: list[int]) -> list[str]: + return [self.tenant_for_id(row_id) for row_id in row_ids] + + def tenant_labels(self) -> list[str]: + return [f"{self.tenant_prefix}{tenant_id:0{self.tenant_id_width}d}" for tenant_id in range(self.tenant_count)] + + @property + def filters(self) -> Filter: + if self.label_percentage is not None: + return LabelFilter(label_percentage=self.label_percentage) + if self.filter_rate is None: + return non_filter + int_field = self.dataset.data.train_id_field + int_value = int(self.dataset.data.size * self.filter_rate) + return NewIntFilter(filter_rate=self.filter_rate, int_field=int_field, int_value=int_value) + + class LabelFilterPerformanceCase(PerformanceCase): case_id: CaseType = CaseType.LabelFilterPerformanceCase dataset_with_size_type: DatasetWithSizeType @@ -631,6 +921,71 @@ def filters(self) -> Filter: return LabelFilter(label_percentage=self.label_percentage) +class FtsPerformanceCase(Case): + """Base class for full-text search BM25 performance cases.""" + + label: CaseLabel = CaseLabel.FullTextSearchPerformance + dataset: FtsDatasetManager + + filter_rate: float | None = None + + @property + def filters(self) -> Filter: + if self.filter_rate is None: + return non_filter + int_value = int(self.dataset.data.size * self.filter_rate) + return NewIntFilter(filter_rate=self.filter_rate, int_field=FTS_FILTER_ID_FIELD, int_value=int_value) + + def estimated_payload_bytes_per_query(self, k: int | None) -> int: + if k is None: + k = config.K_DEFAULT + return self.payload_profile.estimated_bytes_per_query(k=k, dim=0) + + +class FTSBm25Performance(FtsPerformanceCase): + case_id: CaseType = CaseType.FTSBm25Performance + dataset_with_size_type: FtsDatasetWithSizeType = FtsDatasetWithSizeType.MSMarcoSmall + + def __init__( + self, + dataset_with_size_type: FtsDatasetWithSizeType | str = FtsDatasetWithSizeType.MSMarcoSmall, + filter_rate: float | None = None, + **kwargs, + ): + if not isinstance(dataset_with_size_type, FtsDatasetWithSizeType): + dataset_with_size_type = FtsDatasetWithSizeType(dataset_with_size_type) + if filter_rate is not None: + if not dataset_with_size_type.is_advanced: + msg = "FTS filter cases are only supported for MS MARCO Large and HotpotQA Large" + raise ValueError(msg) + if not _is_supported_fts_filter_rate(filter_rate): + supported_rates = ", ".join(_format_filter_rate(rate) for rate in FTS_FILTER_RATES) + msg = f"FTS filter_rate must be one of: {supported_rates}" + raise ValueError(msg) + dataset = dataset_with_size_type.get_manager() + filter_suffix = f", Filter {_format_filter_rate(filter_rate)}" if filter_rate is not None else "" + name = f"FTS BM25 Performance - {dataset_with_size_type.value}{filter_suffix}" + description = ( + f"This case tests native BM25 full-text search performance on {dataset_with_size_type.value}. " + "It measures index building time, recall, serial latency, and search QPS." + ) + if filter_rate is not None: + description += ( + f" The FTS filter case searches only documents with {FTS_FILTER_ID_FIELD} >= " + f"int(dataset_size * {filter_rate})." + ) + super().__init__( + name=name, + description=description, + dataset=dataset, + dataset_with_size_type=dataset_with_size_type, + filter_rate=filter_rate, + load_timeout=dataset_with_size_type.get_load_timeout(), + optimize_timeout=dataset_with_size_type.get_optimize_timeout(), + **kwargs, + ) + + type2case = { CaseType.CapacityDim960: CapacityDim960, CaseType.CapacityDim128: CapacityDim128, @@ -655,4 +1010,9 @@ def filters(self) -> Filter: CaseType.StreamingCustomDataset: StreamingCustomDataset, CaseType.NewIntFilterPerformanceCase: NewIntFilterPerformanceCase, CaseType.LabelFilterPerformanceCase: LabelFilterPerformanceCase, + CaseType.CloudPayloadSearchCase: CloudPayloadSearchCase, + CaseType.FTSBm25Performance: FTSBm25Performance, + CaseType.CloudInsertCase: CloudInsertCase, + CaseType.CloudColdLatencyCase: CloudColdLatencyCase, + CaseType.CloudMultiTenantSearchCase: CloudMultiTenantSearchCase, } diff --git a/vectordb_bench/backend/clients/__init__.py b/vectordb_bench/backend/clients/__init__.py index ebb3b3b13..81c65c4e0 100644 --- a/vectordb_bench/backend/clients/__init__.py +++ b/vectordb_bench/backend/clients/__init__.py @@ -59,6 +59,12 @@ class DB(Enum): Zvec = "Zvec" Endee = "Endee" Lindorm = "Lindorm" + VectorChord = "VectorChord" + PolarDB = "PolarDB" + Pinot = "Pinot" + SeekDB = "SeekDB" + VolcMySQL = "VolcMySQL" + Adbpg = "AnalyticDB for PostgreSQL" Antfly = "Antfly" @property @@ -247,6 +253,35 @@ def init_cls(self) -> type[VectorDB]: # noqa: PLR0911, PLR0912, C901, PLR0915 return LindormVector + if self == DB.VectorChord: + from .vectorchord.vectorchord import VectorChord + + return VectorChord + if self == DB.PolarDB: + from .polardb.polardb import PolarDB + + return PolarDB + + if self == DB.Pinot: + from .pinot.pinot import Pinot + + return Pinot + + if self == DB.SeekDB: + from .seekdb.seekdb import SeekDB + + return SeekDB + + if self == DB.VolcMySQL: + from .volc_mysql.volc_mysql import VolcMySQL + + return VolcMySQL + + if self == DB.Adbpg: + from .adbpg.adbpg import Adbpg + + return Adbpg + if self == DB.Antfly: from .antfly.antfly import Antfly @@ -441,6 +476,35 @@ def config_cls(self) -> type[DBConfig]: # noqa: PLR0911, PLR0912, C901, PLR0915 return LindormConfig + if self == DB.VectorChord: + from .vectorchord.config import VectorChordConfig + + return VectorChordConfig + if self == DB.PolarDB: + from .polardb.config import PolarDBConfig + + return PolarDBConfig + + if self == DB.Pinot: + from .pinot.config import PinotConfig + + return PinotConfig + + if self == DB.SeekDB: + from .seekdb.config import SeekDBConfig + + return SeekDBConfig + + if self == DB.VolcMySQL: + from .volc_mysql.config import VolcMySQLConfig + + return VolcMySQLConfig + + if self == DB.Adbpg: + from .adbpg.config import AdbpgConfig + + return AdbpgConfig + if self == DB.Antfly: from .antfly.config import AntflyConfig @@ -459,11 +523,19 @@ def case_config_cls( # noqa: C901, PLR0911, PLR0912, PLR0915 return _milvus_case_config.get(index_type) if self == DB.ZillizCloud: + if index_type == IndexType.FTS: + from .zilliz_cloud.config import ZillizCloudFtsConfig + + return ZillizCloudFtsConfig from .zilliz_cloud.config import AutoIndexConfig return AutoIndexConfig if self == DB.ElasticCloud: + if index_type == IndexType.FTS: + from .elastic_cloud.config import ElasticCloudFtsConfig + + return ElasticCloudFtsConfig from .elastic_cloud.config import ElasticCloudIndexConfig return ElasticCloudIndexConfig @@ -499,6 +571,10 @@ def case_config_cls( # noqa: C901, PLR0911, PLR0912, PLR0915 return AWSOpenSearchIndexConfig if self == DB.OSSOpenSearch: + if index_type == IndexType.FTS: + from .oss_opensearch.config import OSSOpenSearchFtsConfig + + return OSSOpenSearchFtsConfig from .oss_opensearch.config import OSSOpenSearchIndexConfig return OSSOpenSearchIndexConfig @@ -559,6 +635,10 @@ def case_config_cls( # noqa: C901, PLR0911, PLR0912, PLR0915 return _cockroachdb_case_config.get(index_type) if self == DB.Vespa: + if index_type == IndexType.FTS: + from .vespa.config import VespaFtsConfig + + return VespaFtsConfig from .vespa.config import VespaHNSWConfig return VespaHNSWConfig @@ -592,12 +672,21 @@ def case_config_cls( # noqa: C901, PLR0911, PLR0912, PLR0915 return AliSQLIndexConfig + if self == DB.PolarDB: + from .polardb.config import _polardb_case_config + + return _polardb_case_config.get(index_type) + if self == DB.Doris: from .doris.config import DorisCaseConfig return DorisCaseConfig if self == DB.TurboPuffer: + if index_type == IndexType.FTS: + from .turbopuffer.config import TurboPufferFtsConfig + + return TurboPufferFtsConfig from .turbopuffer.config import TurboPufferIndexConfig return TurboPufferIndexConfig @@ -612,6 +701,35 @@ def case_config_cls( # noqa: C901, PLR0911, PLR0912, PLR0915 return _lindorm_vector_case_config.get(index_type) + if self == DB.VectorChord: + from .vectorchord.config import _vectorchord_case_config + + return _vectorchord_case_config.get(index_type) + + if self == DB.Pinot: + from .pinot.config import PinotHNSWConfig, PinotIVFFlatConfig, PinotIVFPQConfig + + return { + IndexType.HNSW: PinotHNSWConfig, + IndexType.IVFFlat: PinotIVFFlatConfig, + IndexType.IVFPQ: PinotIVFPQConfig, + }.get(index_type, PinotHNSWConfig) + + if self == DB.SeekDB: + from .seekdb.config import _seekdb_case_config + + return _seekdb_case_config.get(index_type) + + if self == DB.VolcMySQL: + from .volc_mysql.config import _volcmysql_case_config + + return _volcmysql_case_config.get(index_type) + + if self == DB.Adbpg: + from .adbpg.config import AdbpgIndexConfig + + return AdbpgIndexConfig + if self == DB.Antfly: from .antfly.config import AntflyIndexConfig diff --git a/vectordb_bench/backend/clients/adbpg/__init__.py b/vectordb_bench/backend/clients/adbpg/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vectordb_bench/backend/clients/adbpg/adbpg.py b/vectordb_bench/backend/clients/adbpg/adbpg.py new file mode 100644 index 000000000..bc5ac3486 --- /dev/null +++ b/vectordb_bench/backend/clients/adbpg/adbpg.py @@ -0,0 +1,373 @@ +"""Wrapper around the Aliyun ADBPG (AnalyticDB for PostgreSQL) vector database.""" + +import logging +from collections.abc import Generator, Sequence +from contextlib import contextmanager +from typing import Any + +import numpy as np +import psycopg +from pgvector.psycopg import register_vector +from psycopg import Connection, Cursor, sql + +from vectordb_bench.backend.filter import Filter, FilterOp + +from ..api import VectorDB +from .config import AdbpgConfigDict, AdbpgIndexConfig + +log = logging.getLogger(__name__) + + +class Adbpg(VectorDB): + """ADBPG vector database client, using psycopg.""" + + # psycopg Cursor is not thread-safe and the COPY protocol cannot be + # interleaved on a shared connection. Match PgVector/VectorChord and + # let ConcurrentInsertRunner clamp max_workers=1. + thread_safe: bool = False + + supported_filter_types: list[FilterOp] = [ + FilterOp.NonFilter, + FilterOp.NumGE, + FilterOp.StrEqual, + ] + + conn: psycopg.Connection[Any] | None = None + cursor: psycopg.Cursor[Any] | None = None + + _search: sql.Composed + + def __init__( + self, + dim: int, + db_config: AdbpgConfigDict, + db_case_config: AdbpgIndexConfig, + drop_old: bool = False, + with_scalar_labels: bool = False, + **kwargs, + ): + self.name = "Adbpg" + self.case_config = db_case_config + # Allow the framework layer (task_runner) to inject a case-specific table + # name via the `collection_name` kwarg (see Doris for the same pattern). + override_name = kwargs.get("collection_name") + self.table_name = override_name if override_name else db_config["table_name"] + self.connect_config = db_config["connect_config"] + self.dim = dim + self.with_scalar_labels = with_scalar_labels + + self._primary_field = "id" + self._vector_field = "embedding" + self._scalar_label_field = "label" + # Index name derives from the table name + algorithm, e.g. vector_1024d_10m_novamr_index. + self._index_name = f"{self.table_name}_{self.case_config.algorithm}_index" + + self.where_clause = "" + + # construct basic units + self.conn, self.cursor = self._create_connection(**self.connect_config) + + log.info(f"{self.name} config values: {self.connect_config}\n{self.case_config}") + if not any( + ( + self.case_config.create_index_before_load, + self.case_config.create_index_after_load, + ), + ): + msg = ( + f"{self.name} config must create an index using create_index_before_load or create_index_after_load" + f"{self.name} config values: {self.connect_config}\n{self.case_config}" + ) + log.error(msg) + raise RuntimeError(msg) + + if drop_old: + self._drop_index() + self._drop_table() + self._create_table(dim) + if self.case_config.create_index_before_load: + self._create_index() + + self.cursor.close() + self.conn.close() + self.cursor = None + self.conn = None + + @staticmethod + def _create_connection(**kwargs) -> tuple[Connection, Cursor]: + conn = psycopg.connect(**kwargs) + register_vector(conn) + conn.autocommit = False + cursor = conn.cursor() + + assert conn is not None, "Connection is not initialized" + assert cursor is not None, "Cursor is not initialized" + + return conn, cursor + + def _generate_search_query(self) -> sql.Composed: + search_param = self.case_config.search_param() + distance_operator = { + "l2": "<->", + "ip": "<#>", + "cosine": "<=>", + }.get(search_param["metric"], "<->") + + where_clause = sql.SQL(self.where_clause) if self.where_clause else sql.SQL("") + + return sql.Composed( + [ + sql.SQL( + "SELECT {primary_field} FROM public.{table_name} {where_clause} ORDER BY {vector_field} ", + ).format( + table_name=sql.Identifier(self.table_name), + primary_field=sql.Identifier(self._primary_field), + where_clause=where_clause, + vector_field=sql.Identifier(self._vector_field), + ), + sql.SQL(distance_operator), + sql.SQL(" {search_vector}::vector({dim}) LIMIT %s::int").format( + search_vector=sql.Placeholder(), + dim=self.dim, + ), + ], + ) + + @contextmanager + def init(self) -> Generator[None, None, None]: + """Open a session, apply GUCs, yield, then close.""" + self.conn, self.cursor = self._create_connection(**self.connect_config) + + session_options: Sequence[dict[str, Any]] = self.case_config.session_param()["session_options"] + + if len(session_options) > 0: + for setting in session_options: + command = sql.SQL("SET {setting_name} = {val};").format( + setting_name=sql.Identifier(setting["parameter"]["setting_name"]), + val=sql.Identifier(str(setting["parameter"]["val"])), + ) + log.debug(command.as_string(self.cursor)) + self.cursor.execute(command) + self.conn.commit() + + try: + yield + finally: + self.cursor.close() + self.conn.close() + self.cursor = None + self.conn = None + + def _drop_table(self): + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + log.info(f"{self.name} client drop table : {self.table_name}") + + self.cursor.execute( + sql.SQL("DROP TABLE IF EXISTS public.{table_name}").format( + table_name=sql.Identifier(self.table_name), + ), + ) + self.conn.commit() + + def optimize(self, data_size: int | None = None): + self._post_insert() + + def _post_insert(self): + log.info(f"{self.name} post insert before optimize") + if self.case_config.create_index_after_load: + self._drop_index() + self._create_index() + + def _drop_index(self): + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + log.info(f"{self.name} client drop index : {self._index_name}") + + drop_index_sql = sql.SQL("DROP INDEX IF EXISTS {index_name}").format( + index_name=sql.Identifier(self._index_name), + ) + log.debug(drop_index_sql.as_string(self.cursor)) + self.cursor.execute(drop_index_sql) + self.conn.commit() + + def _set_parallel_index_build_param(self): + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + + index_param = self.case_config.index_param() + + if index_param["build_parallel_processes"] is not None: + self.cursor.execute( + sql.SQL("SET fastann.build_parallel_processes TO {};").format( + index_param["build_parallel_processes"], + ), + ) + self.conn.commit() + + results = self.cursor.execute(sql.SQL("SHOW fastann.build_parallel_processes;")).fetchall() + log.info(f"{self.name} parallel index creation parameters: {results}") + + def _create_index(self): + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + log.info(f"{self.name} client create index : {self._index_name}") + + index_param = self.case_config.index_param() + self._set_parallel_index_build_param() + + # Pre-build GUC: raise optimizer level before creating the ANN index. + self.cursor.execute(sql.SQL("SET fastann.nova_build_optimize_level = 3;")) + self.conn.commit() + + options = [] + options.append(sql.SQL("dim = {dim}").format(dim=sql.Literal(self.dim))) + options.append( + sql.SQL("distancemeasure = {measure}").format( + measure=sql.Identifier(index_param["metric"]), + ), + ) + + for option in index_param["index_creation_with_options"]: + if option["val"] is not None: + # When `raw` is set, emit the value as a bare SQL token + # (e.g. auto_reduction=on) instead of a quoted literal. + rendered_val = sql.SQL(str(option["val"])) if option.get("raw") else sql.Literal(option["val"]) + options.append( + sql.SQL("{option_name} = {val}").format( + option_name=sql.Identifier(option["option_name"]), + val=rendered_val, + ), + ) + + with_clause = sql.SQL("WITH ({});").format(sql.SQL(", ").join(options)) if options else sql.Composed(()) + + # Covering index: always INCLUDE the primary field (e.g. id). + index_create_sql = sql.SQL( + """ + CREATE INDEX IF NOT EXISTS {index_name} ON public.{table_name} + USING ann ({vector_field}) INCLUDE ({primary_field}) + """, + ).format( + index_name=sql.Identifier(self._index_name), + table_name=sql.Identifier(self.table_name), + vector_field=sql.Identifier(self._vector_field), + primary_field=sql.Identifier(self._primary_field), + ) + + full_sql = (index_create_sql + with_clause).join(" ") + log.debug(full_sql.as_string(self.cursor)) + self.cursor.execute(full_sql) + self.conn.commit() + + def _create_table(self, dim: int): + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + + try: + log.info(f"{self.name} client create table : {self.table_name}") + + if self.with_scalar_labels: + self.cursor.execute( + sql.SQL( + """ + CREATE TABLE IF NOT EXISTS public.{table_name} + ({primary_field} BIGINT PRIMARY KEY, embedding vector({dim}), {label_field} VARCHAR(64)); + """, + ).format( + table_name=sql.Identifier(self.table_name), + primary_field=sql.Identifier(self._primary_field), + dim=dim, + label_field=sql.Identifier(self._scalar_label_field), + ), + ) + else: + self.cursor.execute( + sql.SQL( + """ + CREATE TABLE IF NOT EXISTS public.{table_name} + ({primary_field} BIGINT PRIMARY KEY, embedding vector({dim})); + """, + ).format( + table_name=sql.Identifier(self.table_name), + primary_field=sql.Identifier(self._primary_field), + dim=dim, + ), + ) + + self.cursor.execute( + sql.SQL( + "ALTER TABLE public.{table_name} ALTER COLUMN embedding SET STORAGE PLAIN;", + ).format(table_name=sql.Identifier(self.table_name)), + ) + self.conn.commit() + except Exception as e: + log.warning(f"Failed to create adbpg table: {self.table_name} error: {e}") + raise e from None + + def insert_embeddings( + self, + embeddings: list[list[float]], + metadata: list[int], + labels_data: list[str] | None = None, + **kwargs: Any, + ) -> tuple[int, Exception | None]: + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + if self.with_scalar_labels: + assert labels_data is not None, "labels_data should be provided if with_scalar_labels is set to True" + + try: + metadata_arr = np.array(metadata) + embeddings_arr = np.array(embeddings) + + with self.cursor.copy( + sql.SQL("COPY public.{table_name} FROM STDIN (FORMAT BINARY)").format( + table_name=sql.Identifier(self.table_name), + ), + ) as copy: + for i, row in enumerate(metadata_arr): + if self.with_scalar_labels: + copy.set_types(["bigint", "vector", "varchar"]) + copy.write_row((row, embeddings_arr[i], labels_data[i])) + else: + copy.set_types(["bigint", "vector"]) + copy.write_row((row, embeddings_arr[i])) + self.conn.commit() + + return len(metadata), None + except Exception as e: + log.warning(f"Failed to insert data into adbpg table ({self.table_name}), error: {e}") + return 0, e + + def prepare_filter(self, filters: Filter): + if filters.type == FilterOp.NonFilter: + self.where_clause = "" + elif filters.type == FilterOp.NumGE: + self.where_clause = f"WHERE {self._primary_field} >= {filters.int_value}" + elif filters.type == FilterOp.StrEqual: + self.where_clause = f"WHERE {self._scalar_label_field} = '{filters.label_value}'" + else: + msg = f"Not support Filter for Adbpg - {filters}" + raise ValueError(msg) + + self._search = self._generate_search_query() + + def search_embedding( + self, + query: list[float], + k: int = 100, + timeout: int | None = None, + **kwargs: Any, + ) -> list[int]: + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + + q = np.asarray(query) + result = self.cursor.execute( + self._search, + (q, k), + prepare=True, + binary=True, + ) + return [int(i[0]) for i in result.fetchall()] diff --git a/vectordb_bench/backend/clients/adbpg/cli.py b/vectordb_bench/backend/clients/adbpg/cli.py new file mode 100644 index 000000000..e579f9028 --- /dev/null +++ b/vectordb_bench/backend/clients/adbpg/cli.py @@ -0,0 +1,205 @@ +import os +from typing import Annotated, Unpack + +import click +from pydantic import SecretStr + +from vectordb_bench.backend.clients import DB + +from ....cli.cli import ( + CommonTypedDict, + cli, + click_parameter_decorators_from_typed_dict, + get_custom_case_config, + run, +) + + +class AdbpgTypedDict(CommonTypedDict): + user_name: Annotated[ + str, + click.option("--user-name", type=str, help="Db username", required=True), + ] + password: Annotated[ + str, + click.option( + "--password", + type=str, + help="Postgres database password", + default=lambda: os.environ.get("POSTGRES_PASSWORD", ""), + show_default="$POSTGRES_PASSWORD", + ), + ] + host: Annotated[str, click.option("--host", type=str, help="Db host", required=True)] + port: Annotated[ + int, + click.option( + "--port", + type=int, + help="Postgres database port", + default=5432, + show_default=True, + required=False, + ), + ] + db_name: Annotated[str, click.option("--db-name", type=str, help="Db name", required=True)] + hnsw_m: Annotated[ + int, + click.option("--hnsw-m", type=int, help="hnsw_m", default=48, show_default=True, required=False), + ] + ef_search: Annotated[ + int, + click.option("--ef-search", type=int, help="ef_search", default=150, show_default=True, required=False), + ] + ef_construction: Annotated[ + int, + click.option( + "--ef-construction", + type=int, + help="ef_construction", + default=600, + show_default=True, + required=False, + ), + ] + nlist: Annotated[ + int, + click.option("--nlist", type=int, help="nlist", default=1024, show_default=True, required=False), + ] + rabitq_bits: Annotated[ + int, + click.option("--rabitq-bits", type=int, help="rabitq_bits", default=7, show_default=True, required=False), + ] + quantize_rescore_amp: Annotated[ + float, + click.option( + "--quantize-rescore-amp", + type=float, + help="fastann.quantize_rescore_amp", + default=0.0, + show_default=True, + required=False, + ), + ] + nova_adaptive_gamma: Annotated[ + float, + click.option( + "--nova-adaptive-gamma", + type=float, + help="fastann.nova_adaptive_gamma", + default=0.0, + show_default=True, + required=False, + ), + ] + auto_reduction: Annotated[ + bool, + click.option( + "--auto-reduction/--no-auto-reduction", + "auto_reduction", + type=bool, + help="Index WITH auto_reduction=on when enabled", + default=False, + show_default=True, + required=False, + ), + ] + max_scan_points: Annotated[ + int, + click.option( + "--max-scan-points", + type=int, + help="max_scan_points", + default=20000, + show_default=True, + required=False, + ), + ] + index_scan_mode: Annotated[ + str, + click.option( + "--index-scan-mode", + type=str, + help="fastann.index_scan_mode", + default="snapshot", + show_default=True, + required=False, + ), + ] + algorithm: Annotated[ + str, + click.option( + "--algorithm", + type=str, + help="algorithm", + default="novamr", + show_default=True, + required=False, + ), + ] + build_parallel_processes: Annotated[ + int, + click.option( + "--build-parallel-processes", + type=int, + help="Sets the maximum process to build index", + required=False, + ), + ] + pca_dim: Annotated[ + int | None, + click.option( + "--pca-dim", + type=int, + help="PCA dimension for index dimensionality reduction", + default=None, + show_default=True, + required=False, + ), + ] + nprobe: Annotated[ + int, + click.option( + "--nprobe", + type=int, + help="fastann.nova_nprobe (novad search)", + default=5, + show_default=True, + required=False, + ), + ] + + +@cli.command() +@click_parameter_decorators_from_typed_dict(AdbpgTypedDict) +def AdbpgNova(**parameters: Unpack[AdbpgTypedDict]): + from .config import AdbpgConfig, AdbpgIndexConfig + + parameters["custom_case"] = get_custom_case_config(parameters) + run( + db=DB.Adbpg, + db_config=AdbpgConfig( + user_name=SecretStr(parameters["user_name"]), + password=SecretStr(parameters["password"]), + host=parameters["host"], + port=parameters["port"], + db_name=parameters["db_name"], + ), + db_case_config=AdbpgIndexConfig( + hnsw_m=parameters["hnsw_m"], + ef_search=parameters["ef_search"], + ef_construction=parameters["ef_construction"], + nlist=parameters["nlist"], + algorithm=parameters["algorithm"], + build_parallel_processes=parameters["build_parallel_processes"], + rabitq_bits=parameters["rabitq_bits"], + quantize_rescore_amp=parameters["quantize_rescore_amp"], + nova_adaptive_gamma=parameters["nova_adaptive_gamma"], + auto_reduction=parameters["auto_reduction"], + pca_dim=parameters["pca_dim"], + max_scan_points=parameters["max_scan_points"], + index_scan_mode=parameters["index_scan_mode"], + nprobe=parameters["nprobe"], + ), + **parameters, + ) diff --git a/vectordb_bench/backend/clients/adbpg/config.py b/vectordb_bench/backend/clients/adbpg/config.py new file mode 100644 index 000000000..cc69085bc --- /dev/null +++ b/vectordb_bench/backend/clients/adbpg/config.py @@ -0,0 +1,137 @@ +from collections.abc import Mapping, Sequence +from typing import Any, TypedDict + +from pydantic import BaseModel, SecretStr + +from ..api import DBCaseConfig, DBConfig, MetricType + + +class AdbpgSessionCommands(TypedDict): + session_options: Sequence[dict[str, Any]] + + +class AdbpgConfigDict(TypedDict): + """These keys will be directly used as kwargs in psycopg connection string, + so the names must match exactly psycopg API.""" + + user: str + password: str + host: str + port: int + dbname: str + + +class AdbpgConfig(DBConfig): + user_name: SecretStr = SecretStr("tester") + password: SecretStr = SecretStr("") + host: str = "localhost" + port: int = 5432 + db_name: str = "postgres" + + def to_dict(self) -> dict: + user_str = self.user_name.get_secret_value() if isinstance(self.user_name, SecretStr) else self.user_name + pwd_str = self.password.get_secret_value() + return { + "table_name": "vector", + "connect_config": { + "host": self.host, + "port": self.port, + "dbname": self.db_name, + "user": user_str, + "password": pwd_str, + "options": "-c gp_session_role=utility", + }, + } + + +class AdbpgIndexConfig(BaseModel, DBCaseConfig): + metric_type: MetricType | None = None + create_index_before_load: bool = False + create_index_after_load: bool = True + + # ADB PG specific parameters + hnsw_m: int = 48 + ef_search: int = 150 + ef_construction: int = 600 + nlist: int = 1024 + algorithm: str = "novamr" + build_parallel_processes: int | None = None + # rabitq quantization params + rabitq_bits: int = 7 + quantize_rescore_amp: float = 0.0 + nova_adaptive_gamma: float = 0.0 + max_scan_points: int = 20000 + index_scan_mode: str = "snapshot" + auto_reduction: bool = False + pca_dim: int | None = None + # novad-specific search param (no-op for novamr/HNSW algorithms) + nprobe: int = 5 + + def parse_metric(self) -> str: + if self.metric_type == MetricType.L2: + return "l2" + if self.metric_type == MetricType.COSINE: + return "cosine" + if self.metric_type == MetricType.IP: + return "ip" + msg = f"Metric type {self.metric_type} is not supported!" + raise ValueError(msg) + + @staticmethod + def _build_forced_set_options(set_mapping: Mapping[str, Any]) -> Sequence[dict[str, Any]]: + """Always emit SET commands regardless of value (including 0 / 0.0).""" + return [ + { + "parameter": { + "setting_name": name, + "val": str(value), + }, + } + for name, value in set_mapping.items() + ] + + def index_param(self) -> dict: + with_options = [ + {"option_name": "algorithm", "val": self.algorithm}, + {"option_name": "hnsw_m", "val": self.hnsw_m}, + {"option_name": "hnsw_ef_construction", "val": self.ef_construction}, + {"option_name": "nlist", "val": self.nlist}, + {"option_name": "rabitq_bits", "val": self.rabitq_bits}, + # Covering index key length. + {"option_name": "max_key_len", "val": 1}, + ] + # Optional: auto_reduction=on — only include when True. + # Uses raw=True so the value 'on' is emitted as a bare identifier + # instead of a quoted string literal. + if self.auto_reduction: + with_options.append({"option_name": "auto_reduction", "val": "on", "raw": True}) + if self.pca_dim is not None: + with_options.append({"option_name": "pca_dim", "val": self.pca_dim}) + + return { + "metric": self.parse_metric(), + "build_parallel_processes": self.build_parallel_processes, + "create_index_before_load": self.create_index_before_load, + "create_index_after_load": self.create_index_after_load, + "index_creation_with_options": with_options, + } + + def search_param(self) -> dict: + return { + "metric": self.parse_metric(), + } + + def session_param(self) -> AdbpgSessionCommands: + # All CLI-driven search GUCs are always sent, regardless of value, + # so that callers can explicitly tune any parameter — including to 0. + session_parameters = { + "fastann.quantize_rescore_amp": self.quantize_rescore_amp, + "fastann.nova_adaptive_gamma": self.nova_adaptive_gamma, + "fastann.hnsw_ef_search": self.ef_search, + "fastann.hnsw_max_scan_points": self.max_scan_points, + "fastann.index_scan_mode": self.index_scan_mode, + "fastann.nova_nprobe": self.nprobe, + "optimizer": "off", + "elog_process_parameters": "off", + } + return {"session_options": self._build_forced_set_options(session_parameters)} diff --git a/vectordb_bench/backend/clients/alisql/alisql.py b/vectordb_bench/backend/clients/alisql/alisql.py index f88cf9d88..196d88731 100644 --- a/vectordb_bench/backend/clients/alisql/alisql.py +++ b/vectordb_bench/backend/clients/alisql/alisql.py @@ -1,7 +1,9 @@ +import array import logging -from contextlib import contextmanager +import queue +from contextlib import contextmanager, suppress -import mysql.connector as mysql +import MySQLdb import numpy as np from ..api import VectorDB @@ -11,6 +13,8 @@ class AliSQL(VectorDB): + thread_safe = True + def __init__( self, dim: int, @@ -25,8 +29,9 @@ def __init__( self.case_config = db_case_config self.table_name = collection_name self.dim = dim + # Pool of extra connections used to parallelize inserts; built in init(). + self._insert_pool: queue.SimpleQueue | None = None - # construct basic units self.conn, self.cursor = self._create_connection() if drop_old: @@ -39,12 +44,11 @@ def __init__( self.conn = None def _create_connection(self): - conn = mysql.connect( + conn = MySQLdb.connect( host=self.db_config["host"], user=self.db_config["user"], port=self.db_config["port"], password=self.db_config["password"], - buffered=True, ) cursor = conn.cursor() @@ -53,13 +57,40 @@ def _create_connection(self): return conn, cursor + def _acquire_insert_conn(self): + """Borrow a connection from the insert pool, opening a new one if empty. + + The pool grows lazily to the number of concurrent insert workers: with N + worker threads at most N connections are checked out at once. + """ + try: + return self._insert_pool.get_nowait() + except queue.Empty: + conn, cursor = self._create_connection() + cursor.execute("SET sql_mode = ''") + return conn, cursor + + def _drain_insert_pool(self): + """Close every pooled insert connection. Called from init()'s finally, after + all insert workers have joined, so nothing is checked out at this point. + """ + pool, self._insert_pool = self._insert_pool, None + if pool is None: + return + while True: + try: + conn, cursor = pool.get_nowait() + except queue.Empty: + break + with suppress(Exception): + cursor.close() + conn.close() + def _drop_db(self): assert self.conn is not None, "Connection is not initialized" assert self.cursor is not None, "Cursor is not initialized" log.info(f'{self.name} client drop db : {self.db_config["database"]}') - # flush tables before dropping database to avoid some locking issue - self.cursor.execute("FLUSH TABLES") self.cursor.execute(f'DROP DATABASE IF EXISTS {self.db_config["database"]}') self.cursor.execute("COMMIT") self.cursor.execute("FLUSH TABLES") @@ -107,25 +138,28 @@ def init(self): self.cursor.execute(f"SET SESSION vidx_hnsw_ef_search = {search_param['ef_search']}") self.cursor.execute("COMMIT") - self.insert_sql = ( - f'INSERT INTO {self.db_config["database"]}.{self.table_name} (id, v) VALUES (%s, %s)' # noqa: S608 - ) + self.insert_sql = f'INSERT INTO {self.db_config["database"]}.{self.table_name} (id, v) VALUES (%s, %s)' self.select_sql = ( - f'SELECT id FROM {self.db_config["database"]}.{self.table_name} ' # noqa: S608 + f'SELECT id FROM {self.db_config["database"]}.{self.table_name} ' f"ORDER by vec_distance_{search_param['metric_type']}(v, %s) LIMIT %s" ) self.select_sql_with_filter = ( - f'SELECT id FROM {self.db_config["database"]}.{self.table_name} WHERE id >= %s ' # noqa: S608 + f'SELECT id FROM {self.db_config["database"]}.{self.table_name} WHERE id >= %s ' f"ORDER by vec_distance_{search_param['metric_type']}(v, %s) LIMIT %s" ) + self._search_cursor = self.cursor + self._insert_pool = queue.SimpleQueue() + try: yield finally: + self._drain_insert_pool() self.cursor.close() self.conn.close() self.cursor = None self.conn = None + self._search_cursor = None def ready_to_load(self) -> bool: pass @@ -140,6 +174,10 @@ def optimize(self, data_size: int) -> None: index_options = f"DISTANCE={index_param['metric_type']}" if index_param["index_type"] == "HNSW" and index_param["M"] is not None: index_options += f" M={index_param['M']}" + if index_param.get("shards") is not None: + index_options += f" SHARDS={index_param['shards']}" + if index_param.get("quantization") is not None: + index_options += f" QUANTIZATION={index_param['quantization']}" self.cursor.execute(f""" ALTER TABLE {self.db_config["database"]}.{self.table_name} @@ -153,7 +191,7 @@ def optimize(self, data_size: int) -> None: @staticmethod def vector_to_hex(v): # noqa: ANN001 - return np.array(v, "float32").tobytes() + return array.array("f", v).tobytes() def insert_embeddings( self, @@ -161,28 +199,29 @@ def insert_embeddings( metadata: list[int], **kwargs, ) -> tuple[int, Exception]: - """Insert embeddings into the database. - Should call self.init() first. - """ - assert self.conn is not None, "Connection is not initialized" - assert self.cursor is not None, "Cursor is not initialized" + """Insert one batch of embeddings. Should call self.init() first. + thread_safe=True: the concurrent insert runner may call this from several + worker threads sharing this instance. Each call borrows its own connection + from self._insert_pool, so the workers drive parallel insert streams. + """ + conn, cursor = self._acquire_insert_conn() try: - metadata_arr = np.array(metadata) - embeddings_arr = np.array(embeddings) + embeddings_f32 = np.asarray(embeddings, dtype=np.float32) + batch_data = [(int(metadata[i]), embeddings_f32[i].tobytes()) for i in range(len(metadata))] - batch_data = [] - for i, row in enumerate(metadata_arr): - batch_data.append((int(row), self.vector_to_hex(embeddings_arr[i]))) - - self.cursor.executemany(self.insert_sql, batch_data) - self.cursor.execute("COMMIT") - self.cursor.execute("FLUSH TABLES") - - return len(metadata), None + cursor.executemany(self.insert_sql, batch_data) + cursor.execute("COMMIT") except Exception as e: log.warning(f"Failed to insert data into Vector table ({self.table_name}), error: {e}") + # the connection may be left in a bad state; drop it instead of reusing + with suppress(Exception): + cursor.close() + conn.close() return 0, e + else: + self._insert_pool.put((conn, cursor)) + return len(metadata), None def search_embedding( self, @@ -193,17 +232,17 @@ def search_embedding( **kwargs, ) -> list[int]: assert self.conn is not None, "Connection is not initialized" - assert self.cursor is not None, "Cursor is not initialized" + assert self._search_cursor is not None, "Cursor is not initialized" - search_param = self.case_config.search_param() # noqa: F841 + query_bytes = self.vector_to_hex(query) try: if filters: - self.cursor.execute(self.select_sql_with_filter, (filters.get("id"), self.vector_to_hex(query), k)) + self._search_cursor.execute(self.select_sql_with_filter, (filters.get("id"), query_bytes, k)) else: - self.cursor.execute(self.select_sql, (self.vector_to_hex(query), k)) - return [row[0] for row in self.cursor.fetchall()] + self._search_cursor.execute(self.select_sql, (query_bytes, k)) + return [row[0] for row in self._search_cursor.fetchall()] - except mysql.Error: + except MySQLdb.Error: log.exception("Failed to execute search query") raise diff --git a/vectordb_bench/backend/clients/alisql/cli.py b/vectordb_bench/backend/clients/alisql/cli.py index aee3ec2b2..55eedaf97 100644 --- a/vectordb_bench/backend/clients/alisql/cli.py +++ b/vectordb_bench/backend/clients/alisql/cli.py @@ -85,6 +85,26 @@ class AliSQLHNSWTypedDict(AliSQLTypedDict): ), ] + shards: Annotated[ + int | None, + click.option( + "--shards", + type=int, + help="Number of shards for the vector index", + required=False, + ), + ] + + quantization: Annotated[ + str | None, + click.option( + "--quantization", + type=click.Choice(["SQ8", "SQ16"], case_sensitive=False), + help="Quantization algorithm for the vector index", + required=False, + ), + ] + @cli.command() @click_parameter_decorators_from_typed_dict(AliSQLHNSWTypedDict) @@ -106,6 +126,8 @@ def AliSQLHNSW( db_case_config=AliSQLHNSWConfig( M=parameters["m"], ef_search=parameters["ef_search"], + shards=parameters["shards"], + quantization=parameters["quantization"].upper() if parameters["quantization"] is not None else None, ), **parameters, ) diff --git a/vectordb_bench/backend/clients/alisql/config.py b/vectordb_bench/backend/clients/alisql/config.py index 16c56e90f..7d384c981 100644 --- a/vectordb_bench/backend/clients/alisql/config.py +++ b/vectordb_bench/backend/clients/alisql/config.py @@ -51,6 +51,8 @@ def parse_metric(self) -> str: class AliSQLHNSWConfig(AliSQLIndexConfig, DBCaseConfig): M: int | None = None ef_search: int | None = None + shards: int | None = None + quantization: str | None = None index: IndexType = IndexType.HNSW def index_param(self) -> dict: @@ -58,6 +60,8 @@ def index_param(self) -> dict: "metric_type": self.parse_metric(), "index_type": self.index.value, "M": self.M, + "shards": self.shards, + "quantization": self.quantization, } def search_param(self) -> dict: diff --git a/vectordb_bench/backend/clients/antfly/antfly.py b/vectordb_bench/backend/clients/antfly/antfly.py index 9523e417e..be9a99c5b 100644 --- a/vectordb_bench/backend/clients/antfly/antfly.py +++ b/vectordb_bench/backend/clients/antfly/antfly.py @@ -1,7 +1,6 @@ import base64 import json import logging -import math import os import struct import time @@ -10,7 +9,9 @@ import httpx -from ..api import DBCaseConfig, MetricType, VectorDB +from ...filter import Filter, FilterOp +from ...payload import PayloadProfile +from ..api import DBCaseConfig, VectorDB log = logging.getLogger(__name__) @@ -35,6 +36,12 @@ def _pack_dense_f32(values: list[float]) -> str: def _make_client(base_url: str, timeout: float) -> httpx.Client: + raw_timeout = os.environ.get("ANTFLY_VDBBENCH_HTTP_TIMEOUT") + if raw_timeout: + try: + timeout = float(raw_timeout) + except ValueError: + log.warning("Ignoring invalid ANTFLY_VDBBENCH_HTTP_TIMEOUT=%r", raw_timeout) return httpx.Client( base_url=base_url, timeout=timeout, @@ -58,6 +65,12 @@ def _detect_api_root(host: str, port: int) -> str: class Antfly(VectorDB): + supported_filter_types: list[FilterOp] = [ + FilterOp.NonFilter, + FilterOp.NumGE, + FilterOp.StrEqual, + ] + def __init__( self, dim: int, @@ -65,12 +78,15 @@ def __init__( db_case_config: DBCaseConfig, collection_name: str = "vdbbench", drop_old: bool = False, + with_scalar_labels: bool = False, **kwargs, ): self.db_config = db_config self.case_config = db_case_config self.collection_name = collection_name self.dim = dim + self.with_scalar_labels = with_scalar_labels + self._filter_query: dict[str, Any] | None = None # Antfly v0.1 used /api/v1; current antfly-zig serves the public DB API # at /db/v1. Auto-detect unless ANTFLY_API_ROOT pins it explicitly. @@ -84,6 +100,7 @@ def __init__( self._store_port = db_config.get("store_port") self._use_direct_store_search = bool(db_config.get("use_direct_store_search")) self._pack_query_vectors = bool(db_config.get("pack_query_vectors")) + self._write_sync_level = os.environ.get("ANTFLY_VDBBENCH_SYNC_LEVEL", "write") self._direct_shard_id: str | None = None self._bench_status_last_log = 0.0 num_shards = db_config.get("num_shards", 1) @@ -192,7 +209,10 @@ def _wait_for_write_ready(self, client: httpx.Client): # legacy (Go) binaries return 500 "shard is still initializing" for a # few seconds after table creation. Probe with a throwaway document # (no embedding, so it never lands in the vector index) until a batch - # write succeeds, then delete it. + # write succeeds. The probe doc is intentionally left in place: deleting + # it would leave a tombstone, and a table that has ever deleted a doc + # loses the "all docs visible" fast path. Every dense query would then + # materialize the complete live-doc set as a positive filter. probe_key = "key:__circus_write_probe__" deadline = time.monotonic() + TABLE_READY_TIMEOUT last_error = None @@ -203,10 +223,6 @@ def _wait_for_write_ready(self, client: httpx.Client): json={"inserts": {probe_key: {"id": -1}}, "sync_level": "write"}, ) if r.is_success: - client.post( - f"/tables/{self.collection_name}/batch", - json={"deletes": [probe_key], "sync_level": "write"}, - ) log.info("Write path is ready") return last_error = f"{r.status_code}: {r.text[:120]}" @@ -425,20 +441,9 @@ def _store_base_url(self) -> str: return f"http://{self._store_host}:{self._store_port}" def need_normalize_cosine(self) -> bool: - return True - - def _uses_cosine_distance(self) -> bool: - try: - return self.case_config.index_param().get("distance_metric") == "cosine" - except Exception: - return getattr(self.case_config, "metric_type", None) == MetricType.COSINE - - @staticmethod - def _normalize_vector(vector: list[float]) -> list[float]: - norm = math.sqrt(sum(value * value for value in vector)) - if norm == 0: - return vector - return [value / norm for value in vector] + # Antfly computes cosine norms natively. Returning True would make the + # shared runner normalize every vector before the adapter sees it. + return False @staticmethod def _pack_vector(vector: list[float]) -> str: @@ -459,11 +464,15 @@ def _serialize_insert_vector(self, vector: list[float]) -> str | list[float]: return self._pack_vector(vector) def _metadata_query_body(self, query: list[float], k: int) -> dict[str, Any]: - return { + body = { "embeddings": {"vec": self._serialize_query_vector(query)}, "limit": k, + "fields": [], **self.case_config.search_param(), } + if getattr(self, "_filter_query", None) is not None: + body["filter_query"] = self._filter_query + return body def _store_query_body(self, query: list[float], k: int) -> dict[str, Any]: search_params = self.case_config.search_param() @@ -525,31 +534,49 @@ def optimize(self, data_size: int | None = None): self._wait_for_index_ready(client, expected_total=data_size) self._maybe_log_bench_status(client, "optimize_end", force=True) + def prepare_filter(self, filters: Filter): + if filters.type == FilterOp.NonFilter: + self._filter_query = None + elif filters.type == FilterOp.NumGE: + self._filter_query = { + "numeric_range": { + "field": filters.int_field, + "min": filters.int_value, + "inclusive_min": True, + } + } + elif filters.type == FilterOp.StrEqual: + self._filter_query = {"term": {filters.label_field: filters.label_value}} + else: + msg = f"Unsupported Antfly filter: {filters}" + raise ValueError(msg) + def insert_embeddings( self, embeddings: list[list[float]], metadata: list[int], + labels_data: list[str] | None = None, **kwargs: Any, ) -> tuple[int, Exception]: total = len(embeddings) + if self.with_scalar_labels and labels_data is None: + return 0, ValueError("Antfly label-filter load requires labels_data") try: - use_cosine = self._uses_cosine_distance() for start in range(0, total, BATCH_CHUNK_SIZE): end = min(start + BATCH_CHUNK_SIZE, total) inserts = {} for i in range(start, end): key = f"key:{metadata[i]}" - embedding = embeddings[i] - if use_cosine: - embedding = self._normalize_vector(embedding) - serialized_embedding = self._serialize_insert_vector(embedding) + serialized_embedding = self._serialize_insert_vector(embeddings[i]) inserts[key] = { "id": metadata[i], "metadata": metadata[i], SOURCE_FIELD: str(metadata[i]), "_embeddings": {"vec": serialized_embedding}, } - payload = {"inserts": inserts, "sync_level": "write"} + if self.with_scalar_labels: + inserts[key]["labels"] = labels_data[i] + payload = {"inserts": inserts, "sync_level": self._write_sync_level} r = self.client.post(f"/tables/{self.collection_name}/batch", json=payload) r.raise_for_status() self._maybe_log_bench_status(self.client, "insert") @@ -562,12 +589,16 @@ def search_embedding( self, query: list[float], k: int = 100, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, filters: dict | None = None, timeout: int | None = None, **kwargs: Any, ) -> list[int]: - if self._uses_cosine_distance(): - query = self._normalize_vector(query) + if payload_profile != PayloadProfile.IDS_ONLY: + msg = f"Antfly VDBBench adapter only supports payload_profile={PayloadProfile.IDS_ONLY.value}" + raise NotImplementedError(msg) + if self._use_direct_store_search and self._filter_query is not None: + raise ValueError("Antfly filtered ANN requires the public metadata query API") if self._use_direct_store_search: if self._direct_shard_id is None: diff --git a/vectordb_bench/backend/clients/antfly/cli.py b/vectordb_bench/backend/clients/antfly/cli.py index 2c8566521..25029c30a 100644 --- a/vectordb_bench/backend/clients/antfly/cli.py +++ b/vectordb_bench/backend/clients/antfly/cli.py @@ -7,9 +7,12 @@ CommonTypedDict, cli, click_parameter_decorators_from_typed_dict, + get_custom_case_config, run, ) +from ...cases import CaseType from .. import DB +from ..api import MetricType class AntflyTypedDict(TypedDict): @@ -56,6 +59,15 @@ class AntflyTypedDict(TypedDict): show_default=True, ), ] + metric_type: Annotated[ + str | None, + click.option( + "--metric-type", + type=click.Choice([metric.value for metric in MetricType]), + help="Distance metric override. Defaults to the selected VDBBench case dataset metric.", + default=None, + ), + ] class AntflyAKNNTypedDict(CommonTypedDict, AntflyTypedDict): ... @@ -66,6 +78,12 @@ class AntflyAKNNTypedDict(CommonTypedDict, AntflyTypedDict): ... def AntflyAKNN(**parameters: Unpack[AntflyAKNNTypedDict]): from .config import AntflyConfig, AntflyIndexConfig + metric_type = ( + MetricType(parameters["metric_type"]) + if parameters["metric_type"] + else CaseType[parameters["case_type"]].case_cls(get_custom_case_config(parameters)).dataset.data.metric_type + ) + run( db=DB.Antfly, db_config=AntflyConfig( @@ -83,6 +101,7 @@ def AntflyAKNN(**parameters: Unpack[AntflyAKNNTypedDict]): db_case_config=AntflyIndexConfig( num_shards=parameters["num_shards"], search_effort=parameters["search_effort"], + metric_type=metric_type, ), **parameters, ) diff --git a/vectordb_bench/backend/clients/api.py b/vectordb_bench/backend/clients/api.py index 5de201664..bac90b7be 100644 --- a/vectordb_bench/backend/clients/api.py +++ b/vectordb_bench/backend/clients/api.py @@ -1,11 +1,12 @@ from abc import ABC, abstractmethod from contextlib import contextmanager from enum import StrEnum -from typing import Any +from typing import ClassVar -from pydantic import BaseModel, ConfigDict, model_validator +from pydantic import BaseModel, model_validator from vectordb_bench.backend.filter import Filter, FilterOp +from vectordb_bench.backend.payload import PayloadProfile class MetricType(StrEnum): @@ -13,6 +14,7 @@ class MetricType(StrEnum): COSINE = "COSINE" IP = "IP" DP = "DP" + BM25 = "BM25" HAMMING = "HAMMING" JACCARD = "JACCARD" @@ -32,6 +34,7 @@ class IndexType(StrEnum): IVF_RABITQ = "IVF_RABITQ" Flat = "FLAT" AUTOINDEX = "AUTOINDEX" + FTS = "FTS" ES_HNSW = "hnsw" ES_HNSW_INT8 = "int8_hnsw" ES_HNSW_INT4 = "int4_hnsw" @@ -43,9 +46,16 @@ class IndexType(StrEnum): GPU_IVF_PQ = "GPU_IVF_PQ" GPU_CAGRA = "GPU_CAGRA" SCANN = "scann" + VCHORDRQ = "vchordrq" + VCHORDG = "vchordg" SCANN_MILVUS = "SCANN_MILVUS" + SVS_VAMANA = "SVS_VAMANA" + SVS_VAMANA_LVQ = "SVS_VAMANA_LVQ" + SVS_VAMANA_LEANVEC = "SVS_VAMANA_LEANVEC" Hologres_HGraph = "HGraph" Hologres_Graph = "Graph" + IVF_HNSW_SQ = "IVF_HNSW_SQ" + IVF_HNSW_PQ = "IVF_HNSW_PQ" NONE = "NONE" @@ -58,6 +68,29 @@ class SQType(StrEnum): FP32 = "FP32" +class NonRetryableInsertError(RuntimeError): + non_retryable = True + + +class PartialInsertError(NonRetryableInsertError): + def __init__( + self, + message: str, + *, + inserted_count: int, + successful_tenants: dict[str, int] | None = None, + failed_tenant: str | None = None, + failed_tenant_count: int | None = None, + cause: Exception | None = None, + ): + super().__init__(message) + self.inserted_count = inserted_count + self.successful_tenants = successful_tenants or {} + self.failed_tenant = failed_tenant + self.failed_tenant_count = failed_tenant_count + self.__cause__ = cause + + class DBConfig(ABC, BaseModel): """DBConfig contains the connection info of vector database @@ -69,12 +102,13 @@ class DBConfig(ABC, BaseModel): ZillizCloudConfig.db_label = 1cu-perf """ - model_config = ConfigDict(validate_default=True) - db_label: str = "" version: str = "" note: str = "" + # Field names subclasses allow to be empty (optional creds, alt-route fields). + _extra_empty_skip: ClassVar[frozenset[str]] = frozenset() + @staticmethod def common_short_configs() -> list[str]: """ @@ -95,15 +129,14 @@ def to_dict(self) -> dict: @model_validator(mode="before") @classmethod - def not_empty_field(cls, data: Any) -> Any: - if isinstance(data, dict): - skip = set(cls.common_short_configs()) | set(cls.common_long_configs()) - for name, v in data.items(): - if name in skip: - continue - if isinstance(v, str) and len(v) == 0: - msg = f"Empty string for field '{name}'!" - raise ValueError(msg) + def not_empty_field(cls, data: any) -> any: + if not isinstance(data, dict): + return data + skip = set(cls.common_short_configs()) | set(cls.common_long_configs()) | cls._extra_empty_skip + empty = [k for k, v in data.items() if k not in skip and isinstance(v, str) and not v] + if empty: + msg = f"Empty field(s): {', '.join(empty)}" + raise ValueError(msg) return data @@ -149,6 +182,11 @@ class VectorDB(ABC): supported_filter_types: list[FilterOp] = [FilterOp.NonFilter] name: str = "" + # Whether the client can share a single connection across threads. + # If False, concurrent runners will deep-copy the instance and call + # init() per thread instead of sharing the parent connection. + thread_safe: bool = True + @classmethod def filter_supported(cls, filters: Filter) -> bool: """Ensure that the filters are supported before testing filtering cases.""" @@ -206,12 +244,91 @@ def need_normalize_cosine(self) -> bool: """Wheather this database need to normalize dataset to support COSINE""" return False + def supports_payload_profile(self, payload_profile: PayloadProfile) -> bool: + return payload_profile == PayloadProfile.IDS_ONLY + + def has_text_field(self) -> bool: + return False + + def supports_document_payload_profile(self, payload_profile: PayloadProfile) -> bool: + if payload_profile == PayloadProfile.IDS_ONLY: + return True + if payload_profile == PayloadProfile.TEXT: + return self.has_text_field() + return False + + def poll_insert_readiness(self, expected_count: int) -> dict: + return {"fully_searchable": True, "fully_indexed": True, "additional_parameters": {}} + + def set_multitenant_context(self, tenant_labels: list[str]) -> None: + self.multitenant_tenant_labels = tenant_labels + + def supports_multitenant(self) -> bool: + return False + + def validate_multitenant_schema(self) -> None: + return None + + @classmethod + def supports_full_text_search(cls) -> bool: + """Return whether this client implements the full-text search API. + + Backends that return True must implement insert_documents and + search_documents for raw text documents. + """ + return False + + def insert_documents( + self, + texts: list[str], + doc_ids: list[str], + **kwargs, + ) -> tuple[int, Exception | None]: + """Insert raw text documents for full-text search cases. + + Args: + texts(list[str]): raw text documents to index. + doc_ids(list[str]): stable document IDs aligned with texts. + **kwargs(Any): backend or runner specific insert parameters. + + Returns: + tuple[int, Exception | None]: inserted document count and an optional + error. Implementations should return the count of successfully + inserted documents even when reporting a partial failure. + """ + msg = f"{self.name or self.__class__.__name__} does not support full-text document insert" + raise NotImplementedError(msg) + + def search_documents( + self, + query: str, + k: int = 100, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + **kwargs, + ) -> list[str]: + """Search full-text documents and return ranked document IDs. + + Args: + query(str): raw query text. + k(int): number of nearest documents to return. Defaults to 100. + payload_profile(PayloadProfile): response payload shape requested by + the benchmark. The API still returns document IDs only; use + supports_document_payload_profile to reject unsupported profiles. + **kwargs(Any): backend or runner specific search parameters. + + Returns: + list[str]: ranked document IDs for the query. + """ + msg = f"{self.name or self.__class__.__name__} does not support full-text document search" + raise NotImplementedError(msg) + @abstractmethod def insert_embeddings( self, embeddings: list[list[float]], metadata: list[int], labels_data: list[str] | None = None, + tenant_labels_data: list[str] | None = None, **kwargs, ) -> tuple[int, Exception]: """Insert the embeddings to the vector database. The default number of embeddings for @@ -232,6 +349,8 @@ def search_embedding( self, query: list[float], k: int = 100, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + tenant: str | None = None, ) -> list[int]: """Get k most similar embeddings to query vector. diff --git a/vectordb_bench/backend/clients/aws_opensearch/aws_opensearch.py b/vectordb_bench/backend/clients/aws_opensearch/aws_opensearch.py index 8d77d263b..eb0195b88 100644 --- a/vectordb_bench/backend/clients/aws_opensearch/aws_opensearch.py +++ b/vectordb_bench/backend/clients/aws_opensearch/aws_opensearch.py @@ -5,6 +5,7 @@ from opensearchpy import OpenSearch +from vectordb_bench import config from vectordb_bench.backend.filter import Filter, FilterOp from ..api import VectorDB @@ -48,6 +49,7 @@ def __init__( log.info(f"AWS_OpenSearch client config: {self.db_config}") log.info(f"AWS_OpenSearch db case config : {self.case_config}") + self._is_serverless = ".aoss." in self.db_config.get("hosts", [{}])[0].get("host", "") client = OpenSearch(**self.db_config) if drop_old: log.info(f"AWS_OpenSearch client drop old index: {self.index_name}") @@ -80,6 +82,9 @@ def _log_index_creation_info(self) -> None: log.info(f"All case_config parameters: {self.case_config.__dict__}") def _configure_cluster_settings(self, client: OpenSearch) -> None: + if self._is_serverless: + log.info("Skipping cluster settings for OpenSearch Serverless") + return cluster_settings_body = { "persistent": { "knn.algo_param.index_thread_qty": self.case_config.index_thread_qty, @@ -89,17 +94,19 @@ def _configure_cluster_settings(self, client: OpenSearch) -> None: client.cluster.put_settings(body=cluster_settings_body) def _build_index_settings(self) -> dict: - return { + settings = { "index": { "knn": True, "number_of_shards": self.case_config.number_of_shards, "number_of_replicas": self.case_config.number_of_replicas, - "translog.flush_threshold_size": self.case_config.flush_threshold_size, - "knn.advanced.approximate_threshold": "-1", - "knn.algo_param.ef_search": self.case_config.ef_search, }, - "refresh_interval": self.case_config.refresh_interval, } + if not self._is_serverless: + settings["index"]["translog.flush_threshold_size"] = self.case_config.flush_threshold_size + settings["index"]["knn.advanced.approximate_threshold"] = "-1" + settings["index"]["knn.algo_param.ef_search"] = self.case_config.ef_search + settings["refresh_interval"] = self.case_config.refresh_interval + return settings def _build_vector_field_config(self) -> dict: method_config = self.case_config.index_param() @@ -108,6 +115,21 @@ def _build_vector_field_config(self) -> dict: if self.case_config.engine == AWSOS_Engine.s3vector: method_config = {"engine": "s3vector"} + # OpenSearch Serverless does not support 'engine' or 'encoder' in method config + if self._is_serverless and "engine" in method_config: + space_type = method_config.pop("space_type", self.case_config.parse_metric()) + method_config.pop("engine", None) + if "parameters" in method_config: + method_config["parameters"].pop("encoder", None) + vector_field_config = { + "type": "knn_vector", + "dimension": self.dim, + "space_type": space_type, + "method": method_config, + } + log.info(f"Serverless vector field config: {vector_field_config}") + return vector_field_config + if self.case_config.on_disk: space_type = self.case_config.parse_metric() vector_field_config = { @@ -157,6 +179,12 @@ def _build_mappings(self, vector_field_config: dict) -> dict: }, } log.info("Using standard mappings with _source configuration for non-s3vector engines") + + # Serverless stores the benchmark id in a dedicated numeric field (custom _id + # is not supported). Map it as a long so NumGE range filters work. + if self._is_serverless: + mappings["properties"]["id"] = {"type": "long"} + return mappings def _create_opensearch_index(self, client: OpenSearch, settings: dict, mappings: dict) -> None: @@ -210,6 +238,12 @@ def insert_embeddings( num_clients = self.case_config.number_of_indexing_clients or 1 log.info(f"Number of indexing clients from case_config: {num_clients}") + # OpenSearch Serverless requires the single-client path because it does not + # support custom _id and needs the benchmark id stored in _source. + if self._is_serverless: + log.info("Using single client for data insertion (OpenSearch Serverless)") + return self._insert_with_single_client(embeddings, metadata, labels_data) + if num_clients <= 1: log.info("Using single client for data insertion") return self._insert_with_single_client(embeddings, metadata, labels_data) @@ -222,25 +256,49 @@ def _insert_with_single_client( metadata: list[int], labels_data: list[str] | None = None, ) -> tuple[int, Exception]: - insert_data = [] - for i in range(len(embeddings)): - index_data = {"index": {"_index": self.index_name, self.id_col_name: metadata[i]}} - if self.with_scalar_labels and self.case_config.use_routing and labels_data is not None: - index_data["routing"] = labels_data[i] - insert_data.append(index_data) - - other_data = {self.vector_col_name: embeddings[i]} - if self.with_scalar_labels and labels_data is not None: - other_data[self.label_col_name] = labels_data[i] - insert_data.append(other_data) + embeddings_list = list(embeddings) + batch_size = config.NUM_PER_BATCH if self._is_serverless else len(embeddings_list) + if self._is_serverless and batch_size <= 0: + raise ValueError("NUM_PER_BATCH must be greater than 0 for OpenSearch Serverless") + total_inserted = 0 - try: - self.client.bulk(body=insert_data) - return len(embeddings), None - except Exception as e: - log.warning(f"Failed to insert data: {self.index_name} error: {e!s}") - time.sleep(10) - return self._insert_with_single_client(embeddings, metadata, labels_data) + for i in range(0, len(embeddings_list), batch_size): + batch_embeddings = embeddings_list[i : i + batch_size] + batch_metadata = metadata[i : i + batch_size] + batch_labels = labels_data[i : i + batch_size] if labels_data else None + + insert_data = [] + for j in range(len(batch_embeddings)): + if self._is_serverless: + index_data = {"index": {"_index": self.index_name}} + else: + index_data = {"index": {"_index": self.index_name, self.id_col_name: batch_metadata[j]}} + + if self.with_scalar_labels and self.case_config.use_routing and batch_labels is not None: + index_data["routing"] = batch_labels[j] + insert_data.append(index_data) + + other_data = {self.vector_col_name: batch_embeddings[j]} + if self._is_serverless: + other_data["id"] = batch_metadata[j] + if self.with_scalar_labels and batch_labels is not None: + other_data[self.label_col_name] = batch_labels[j] + insert_data.append(other_data) + + try: + self.client.bulk(body=insert_data) + total_inserted += len(batch_embeddings) + except Exception as e: + log.warning(f"Failed to insert batch: {self.index_name} error: {e!s}") + time.sleep(10) + try: + self.client.bulk(body=insert_data) + total_inserted += len(batch_embeddings) + except Exception as retry_e: + log.warning(f"Retry failed for batch: {retry_e!s}") + return total_inserted, retry_e + + return total_inserted, None def _insert_with_multiple_clients( self, @@ -402,24 +460,37 @@ def search_embedding( } try: - resp = self.client.search( - index=self.index_name, - body=body, - size=k, - _source=False, - docvalue_fields=[self.id_col_name], - stored_fields="_none_", - preference="_only_local" if self.case_config.number_of_shards == 1 else None, - routing=self.routing_key, - ) - log.debug(f"Search took: {resp['took']}") - log.debug(f"Search shards: {resp['_shards']}") - log.debug(f"Search hits total: {resp['hits']['total']}") - try: - return [int(h["fields"][self.id_col_name][0]) for h in resp["hits"]["hits"]] - except Exception: - # empty results - return [] + if self._is_serverless: + resp = self.client.search( + index=self.index_name, + body=body, + size=k, + _source=["id"], + preference="_only_local" if self.case_config.number_of_shards == 1 else None, + routing=self.routing_key, + ) + try: + return [int(h["_source"]["id"]) for h in resp["hits"]["hits"]] + except Exception: + return [] + else: + resp = self.client.search( + index=self.index_name, + body=body, + size=k, + _source=False, + docvalue_fields=[self.id_col_name], + stored_fields="_none_", + preference="_only_local" if self.case_config.number_of_shards == 1 else None, + routing=self.routing_key, + ) + log.debug(f"Search took: {resp['took']}") + log.debug(f"Search shards: {resp['_shards']}") + log.debug(f"Search hits total: {resp['hits']['total']}") + try: + return [int(h["fields"][self.id_col_name][0]) for h in resp["hits"]["hits"]] + except Exception: + return [] except Exception as e: log.warning(f"Failed to search: {self.index_name} error: {e!s}") raise e from None @@ -429,7 +500,10 @@ def prepare_filter(self, filters: Filter): if filters.type == FilterOp.NonFilter: self.filter = None elif filters.type == FilterOp.NumGE: - self.filter = {"range": {self.id_col_name: {"gt": filters.int_value}}} + # Serverless stores the benchmark id in the "id" field of _source since it + # does not support custom _id. Filter on that stored field instead of _id. + filter_field = "id" if self._is_serverless else self.id_col_name + self.filter = {"range": {filter_field: {"gt": filters.int_value}}} elif filters.type == FilterOp.StrEqual: self.filter = {"term": {self.label_col_name: filters.label_value}} if self.case_config.use_routing: @@ -468,6 +542,10 @@ def _update_ef_search(self): log.warning(f"Failed to update ef_search parameter: {e}") def _update_replicas(self): + if self._is_serverless: + log.info("Skipping replica updates for OpenSearch Serverless") + return + index_settings = self.client.indices.get_settings(index=self.index_name) current_number_of_replicas = int(index_settings[self.index_name]["settings"]["index"]["number_of_replicas"]) log.info( @@ -490,6 +568,11 @@ def _wait_till_green(self): log.info(f"Index {self.index_name} is green..") def _refresh_index(self): + if self._is_serverless: + log.info("Skipping manual refresh for OpenSearch Serverless, waiting for auto-refresh...") + time.sleep(10) + return + log.debug(f"Starting refresh for index {self.index_name}") while True: try: @@ -505,6 +588,10 @@ def _refresh_index(self): log.debug(f"Completed refresh for index {self.index_name}") def _do_force_merge(self): + if self._is_serverless: + log.info("Skipping force merge for OpenSearch Serverless") + return + log.info(f"Updating the Index thread qty to {self.case_config.index_thread_qty_during_force_merge}.") cluster_settings_body = { @@ -530,6 +617,10 @@ def _do_force_merge(self): log.info(f"Completed force merge for index {self.index_name}") def _load_graphs_to_memory(self, client: OpenSearch): + if self._is_serverless: + log.info("Skipping warmup API for OpenSearch Serverless") + return + if self.case_config.engine != AWSOS_Engine.lucene: log.info("Calling warmup API to load graphs into memory") warmup_endpoint = f"/_plugins/_knn/warmup/{self.index_name}" diff --git a/vectordb_bench/backend/clients/aws_opensearch/cli.py b/vectordb_bench/backend/clients/aws_opensearch/cli.py index 5bc80a687..59bb68a23 100644 --- a/vectordb_bench/backend/clients/aws_opensearch/cli.py +++ b/vectordb_bench/backend/clients/aws_opensearch/cli.py @@ -25,8 +25,14 @@ def optional_secret_str(value: str | None) -> SecretStr | None: class AWSOpenSearchTypedDict(TypedDict): host: Annotated[str, click.option("--host", type=str, help="Db host", required=True)] port: Annotated[int, click.option("--port", type=int, default=80, help="Db Port")] - user: Annotated[str | None, click.option("--user", type=str, help="Db User")] - password: Annotated[str | None, click.option("--password", type=str, help="Db password")] + user: Annotated[str | None, click.option("--user", type=str, help="Db User (not needed for Serverless)")] + password: Annotated[ + str | None, click.option("--password", type=str, help="Db password (not needed for Serverless)") + ] + is_serverless: Annotated[bool, click.option("--serverless", is_flag=True, help="Use OpenSearch Serverless")] + aws_region: Annotated[ + str, click.option("--aws-region", type=str, default="us-east-1", help="AWS region for Serverless") + ] number_of_shards: Annotated[ int, click.option("--number-of-shards", type=int, help="Number of primary shards for the index", default=1), @@ -172,6 +178,12 @@ class AWSOpenSearchHNSWTypedDict(CommonTypedDict, AWSOpenSearchTypedDict, HNSWFl def AWSOpenSearch(**parameters: Unpack[AWSOpenSearchHNSWTypedDict]): from .config import AWSOpenSearchConfig, AWSOpenSearchIndexConfig + is_serverless = parameters.get("serverless", False) + log.info(f"Is Serverless: {is_serverless}") + + if not is_serverless and not parameters.get("user"): + log.warning("Standard OpenSearch mode requires user and password.") + # Set default values for HNSW parameters if not provided and not using s3vector engine = AWSOS_Engine(parameters["engine"]) ef_construction = parameters.get("ef_construction") @@ -192,8 +204,10 @@ def AWSOpenSearch(**parameters: Unpack[AWSOpenSearchHNSWTypedDict]): db_config=AWSOpenSearchConfig( host=parameters["host"], port=parameters["port"], - user=parameters["user"], - password=optional_secret_str(parameters["password"]), + user=parameters.get("user"), + password=optional_secret_str(parameters.get("password")), + is_serverless=is_serverless, + aws_region=parameters.get("aws_region", "us-east-1"), ), db_case_config=AWSOpenSearchIndexConfig( number_of_shards=parameters["number_of_shards"], diff --git a/vectordb_bench/backend/clients/aws_opensearch/config.py b/vectordb_bench/backend/clients/aws_opensearch/config.py index f6c81923d..134da869b 100644 --- a/vectordb_bench/backend/clients/aws_opensearch/config.py +++ b/vectordb_bench/backend/clients/aws_opensearch/config.py @@ -1,8 +1,8 @@ import logging from enum import Enum -from typing import Any +from typing import ClassVar -from pydantic import BaseModel, SecretStr, model_validator +from pydantic import BaseModel, SecretStr from ..api import DBCaseConfig, DBConfig, MetricType @@ -10,12 +10,66 @@ class AWSOpenSearchConfig(DBConfig, BaseModel): + _extra_empty_skip: ClassVar[frozenset[str]] = frozenset({"user", "password", "host"}) + host: str = "" port: int = 80 user: str | None = None password: SecretStr | None = None + is_serverless: bool = False + aws_region: str = "us-east-1" def to_dict(self) -> dict: + if self.is_serverless: + return self._serverless_config() + return self._standard_config() + + def _serverless_config(self) -> dict: + """Configuration for OpenSearch Serverless using AWS SigV4 authentication.""" + log.info(f"Configuring OpenSearch Serverless - Host: {self.host}, Region: {self.aws_region}") + + try: + import boto3 + except ImportError as e: + raise ImportError("boto3 is required for OpenSearch Serverless. Install with: pip install boto3") from e + + try: + from opensearchpy import RequestsHttpConnection + from requests_aws4auth import AWS4Auth + except ImportError as e: + raise ImportError( + "requests-aws4auth is required for OpenSearch Serverless. " + "Install with: pip install requests-aws4auth" + ) from e + + session = boto3.Session() + credentials = session.get_credentials() + if not credentials: + raise ValueError("AWS credentials not found. Please configure AWS credentials.") + + credentials = credentials.get_frozen_credentials() + auth = AWS4Auth( + credentials.access_key, + credentials.secret_key, + self.aws_region, + "aoss", + session_token=credentials.token, + ) + + return { + "hosts": [{"host": self.host, "port": 443}], + "http_auth": auth, + "use_ssl": True, + "verify_certs": True, + "connection_class": RequestsHttpConnection, + "timeout": 600, + "max_retries": 3, + "retry_on_timeout": True, + "http_compress": False, + } + + def _standard_config(self) -> dict: + """Configuration for standard OpenSearch with basic auth.""" use_ssl = self.port == 443 http_auth = ( (self.user, self.password.get_secret_value()) @@ -33,19 +87,6 @@ def to_dict(self) -> dict: "timeout": 600, } - @model_validator(mode="before") - @classmethod - def not_empty_field(cls, data: Any) -> Any: - if isinstance(data, dict): - skip = set(cls.common_short_configs()) | set(cls.common_long_configs()) | {"user", "password", "host"} - for name, v in data.items(): - if name in skip: - continue - if isinstance(v, str) and len(v) == 0: - msg = f"Empty string for field '{name}'!" - raise ValueError(msg) - return data - class AWSOS_Engine(Enum): faiss = "faiss" diff --git a/vectordb_bench/backend/clients/chroma/chroma.py b/vectordb_bench/backend/clients/chroma/chroma.py index 6942f4fc9..909c47966 100644 --- a/vectordb_bench/backend/clients/chroma/chroma.py +++ b/vectordb_bench/backend/clients/chroma/chroma.py @@ -3,6 +3,8 @@ import chromadb +from vectordb_bench.backend.filter import Filter, FilterOp + from ..api import VectorDB from .config import ChromaIndexConfig @@ -17,6 +19,11 @@ class ChromaClient(VectorDB): To change to running in process, modify the HttpClient() in __init__() and init(). """ + supported_filter_types: list[FilterOp] = [ + FilterOp.NonFilter, + FilterOp.NumGE, + ] + def __init__( self, dim: int, @@ -35,13 +42,14 @@ def __init__( if drop_old: try: - client.reset() - except Exception: - drop_old = False log.info(f"Chroma client drop_old collection: {self.collection_name}") + client.delete_collection(self.collection_name) + except Exception as e: + log.info(f"Chroma client collection was not dropped: {self.collection_name}, error: {e!s}") self.client = None self.collection = None + self._where_filter: dict | None = None @contextmanager def init(self): @@ -85,10 +93,17 @@ def search_embedding( self, query: list[float], k: int = 100, filters: dict | None = None, timeout: int | None = None ) -> list[int]: assert self.client is not None, "Please call self.init() before" - if filters: - results = self.collection.query( - query_embeddings=[query], n_results=k, where={"id": {"$gt": filters.get("id")}} - ) + if self._where_filter is not None: + results = self.collection.query(query_embeddings=[query], n_results=k, where=self._where_filter) else: results = self.collection.query(query_embeddings=[query], n_results=k) return [int(idx) for idx in results["ids"][0]] + + def prepare_filter(self, filters: Filter): + if filters.type == FilterOp.NonFilter: + self._where_filter = None + elif filters.type == FilterOp.NumGE: + self._where_filter = {"index": {"$gte": filters.int_value}} + else: + msg = f"Unsupported Chroma filter: {filters}" + raise ValueError(msg) diff --git a/vectordb_bench/backend/clients/chroma/cli.py b/vectordb_bench/backend/clients/chroma/cli.py index 64a2f972f..3445660a8 100644 --- a/vectordb_bench/backend/clients/chroma/cli.py +++ b/vectordb_bench/backend/clients/chroma/cli.py @@ -50,6 +50,7 @@ def Chroma(**parameters: Unpack[ChromaTypeDict]): run( db=DBTYPE, db_config=ChromaConfig( + db_label=parameters["db_label"], user=parameters["user"], password=SecretStr(parameters["password"]) if parameters["password"] else None, host=SecretStr(parameters["host"]), diff --git a/vectordb_bench/backend/clients/chroma/config.py b/vectordb_bench/backend/clients/chroma/config.py index 8c1cac286..a1d9903d1 100644 --- a/vectordb_bench/backend/clients/chroma/config.py +++ b/vectordb_bench/backend/clients/chroma/config.py @@ -26,7 +26,7 @@ def to_dict(self) -> dict: class ChromaIndexConfig(ChromaConfig, DBCaseConfig): - metric_type: MetricType = MetricType.COSINE + metric_type: MetricType = "cosine" m: int = 16 ef_construct: int = 100 ef_search: int | None = 100 diff --git a/vectordb_bench/backend/clients/doris/config.py b/vectordb_bench/backend/clients/doris/config.py index 9caf72eb3..7c79ba728 100644 --- a/vectordb_bench/backend/clients/doris/config.py +++ b/vectordb_bench/backend/clients/doris/config.py @@ -1,5 +1,4 @@ import logging -from typing import Any from pydantic import BaseModel, SecretStr, model_validator @@ -20,8 +19,8 @@ class DorisConfig(DBConfig): @model_validator(mode="before") @classmethod - def not_empty_field(cls, data: Any) -> Any: - return data # allow all fields including empty strings + def not_empty_field(cls, data: any) -> any: + return data def to_dict(self) -> dict: pwd_str = self.password.get_secret_value() diff --git a/vectordb_bench/backend/clients/doris/doris.py b/vectordb_bench/backend/clients/doris/doris.py index 82b3a12da..01984d665 100644 --- a/vectordb_bench/backend/clients/doris/doris.py +++ b/vectordb_bench/backend/clients/doris/doris.py @@ -13,6 +13,8 @@ class Doris(VectorDB): + thread_safe: bool = False + def __init__( self, dim: int, diff --git a/vectordb_bench/backend/clients/elastic_cloud/cli.py b/vectordb_bench/backend/clients/elastic_cloud/cli.py index 55e521d8f..f277a6ff2 100644 --- a/vectordb_bench/backend/clients/elastic_cloud/cli.py +++ b/vectordb_bench/backend/clients/elastic_cloud/cli.py @@ -17,11 +17,60 @@ class ElasticCloudTypedDict(TypedDict): cloud_id: Annotated[ str, - click.option("--cloud-id", type=str, help="Elastic Cloud ID", required=True), + click.option( + "--cloud-id", + type=str, + help="Elastic Cloud ID. Omit when connecting to a self-hosted ES via --host.", + required=False, + default="", + ), + ] + scheme: Annotated[ + str, + click.option( + "--scheme", + type=click.Choice(["http", "https"], case_sensitive=False), + help="Scheme for host-based connection.", + required=False, + default="https", + show_default=True, + ), + ] + host: Annotated[ + str, + click.option( + "--host", + type=str, + help="Elasticsearch host (for self-hosted ES; alternative to --cloud-id).", + required=False, + default="", + ), + ] + port: Annotated[ + int, + click.option( + "--port", + type=int, + help="Elasticsearch port (for host-based connection).", + required=False, + default=9200, + show_default=True, + ), + ] + user: Annotated[ + str, + click.option( + "--user", + type=str, + help="Elasticsearch user.", + required=False, + default="elastic", + show_default=True, + ), ] password: Annotated[ str, - click.option("--password", type=str, help="Elastic Cloud password", required=True), + click.option("--password", type=str, help="Elasticsearch password", required=True), ] number_of_shards: Annotated[ int, @@ -170,7 +219,11 @@ def ElasticCloudHNSW(**parameters: Unpack[ElasticCloudHNSWTypedDict]): db=DBTYPE, db_config=ElasticCloudConfig( db_label=parameters["db_label"], - cloud_id=SecretStr(parameters["cloud_id"]), + cloud_id=SecretStr(parameters["cloud_id"]) if parameters["cloud_id"] else None, + scheme=parameters["scheme"], + host=parameters["host"], + port=parameters["port"], + user=parameters["user"], password=SecretStr(parameters["password"]), ), db_case_config=ElasticCloudIndexConfig( @@ -202,7 +255,11 @@ def ElasticCloudHNSWInt8(**parameters: Unpack[ElasticCloudHNSWTypedDict]): db=DBTYPE, db_config=ElasticCloudConfig( db_label=parameters["db_label"], - cloud_id=SecretStr(parameters["cloud_id"]), + cloud_id=SecretStr(parameters["cloud_id"]) if parameters["cloud_id"] else None, + scheme=parameters["scheme"], + host=parameters["host"], + port=parameters["port"], + user=parameters["user"], password=SecretStr(parameters["password"]), ), db_case_config=ElasticCloudIndexConfig( @@ -234,7 +291,11 @@ def ElasticCloudHNSWInt4(**parameters: Unpack[ElasticCloudHNSWTypedDict]): db=DBTYPE, db_config=ElasticCloudConfig( db_label=parameters["db_label"], - cloud_id=SecretStr(parameters["cloud_id"]), + cloud_id=SecretStr(parameters["cloud_id"]) if parameters["cloud_id"] else None, + scheme=parameters["scheme"], + host=parameters["host"], + port=parameters["port"], + user=parameters["user"], password=SecretStr(parameters["password"]), ), db_case_config=ElasticCloudIndexConfig( @@ -266,7 +327,11 @@ def ElasticCloudHNSWBBQ(**parameters: Unpack[ElasticCloudHNSWTypedDict]): db=DBTYPE, db_config=ElasticCloudConfig( db_label=parameters["db_label"], - cloud_id=SecretStr(parameters["cloud_id"]), + cloud_id=SecretStr(parameters["cloud_id"]) if parameters["cloud_id"] else None, + scheme=parameters["scheme"], + host=parameters["host"], + port=parameters["port"], + user=parameters["user"], password=SecretStr(parameters["password"]), ), db_case_config=ElasticCloudIndexConfig( diff --git a/vectordb_bench/backend/clients/elastic_cloud/config.py b/vectordb_bench/backend/clients/elastic_cloud/config.py index 7ee54b49c..9f9961efc 100644 --- a/vectordb_bench/backend/clients/elastic_cloud/config.py +++ b/vectordb_bench/backend/clients/elastic_cloud/config.py @@ -1,19 +1,69 @@ from enum import StrEnum +from typing import ClassVar -from pydantic import BaseModel, SecretStr +from pydantic import BaseModel, SecretStr, model_validator from ..api import DBCaseConfig, DBConfig, IndexType, MetricType +from ..elasticsearch_compatible import build_bm25_similarity_settings, build_fts_index_param class ElasticCloudConfig(DBConfig, BaseModel): - cloud_id: SecretStr - password: SecretStr + _extra_empty_skip: ClassVar[frozenset[str]] = frozenset( + {"cloud_id", "scheme", "host", "user", "user_name", "password"} + ) + + cloud_id: SecretStr | None = None + scheme: str | None = None + host: SecretStr | None = None + port: int = 9200 + user: str | None = None + user_name: str | None = None + password: SecretStr | None = None + use_ssl: bool = False + verify_certs: bool = True + + @model_validator(mode="after") + def _check_connection_target(self) -> "ElasticCloudConfig": + has_cloud_id = bool(self.cloud_id and self.cloud_id.get_secret_value()) + if not has_cloud_id and not self.host: + msg = "Either cloud_id or host must be set" + raise ValueError(msg) + return self + + def _auth_user(self) -> str: + return self.user_name or self.user or "elastic" def to_dict(self) -> dict: - return { - "cloud_id": self.cloud_id.get_secret_value(), - "basic_auth": ("elastic", self.password.get_secret_value()), + if self.cloud_id and self.cloud_id.get_secret_value(): + if not self.password: + msg = "password is required when cloud_id is set" + raise ValueError(msg) + return { + "cloud_id": self.cloud_id.get_secret_value(), + "basic_auth": (self._auth_user(), self.password.get_secret_value()), + } + + if not self.host: + msg = "Either cloud_id or host must be set" + raise ValueError(msg) + + host = self.host.get_secret_value() + if host.startswith(("http://", "https://")): + url = host + else: + scheme = self.scheme or ("https" if self.use_ssl else "http") + url = f"{scheme}://{host}:{self.port}" + + config = { + "hosts": [url], + "verify_certs": self.verify_certs, } + if self.password: + config["basic_auth"] = (self._auth_user(), self.password.get_secret_value()) + elif self.user_name or (self.user and self.user != "elastic"): + msg = "password is required when user_name is set" + raise ValueError(msg) + return config class ESElementType(StrEnum): @@ -85,3 +135,22 @@ def search_param(self) -> dict: return { "num_candidates": self.num_candidates, } + + +class ElasticCloudFtsConfig(BaseModel, DBCaseConfig): + number_of_shards: int = 1 + number_of_replicas: int = 0 + refresh_interval: str = "30s" + use_force_merge: bool = True + metric_type: MetricType = MetricType.BM25 + bm25_k1: float | None = None + bm25_b: float | None = None + + def index_param(self) -> dict: + return build_fts_index_param(self.bm25_k1, self.bm25_b) + + def search_param(self) -> dict: + return {} + + def similarity_settings(self) -> dict: + return build_bm25_similarity_settings(self.bm25_k1, self.bm25_b) diff --git a/vectordb_bench/backend/clients/elastic_cloud/elastic_cloud.py b/vectordb_bench/backend/clients/elastic_cloud/elastic_cloud.py index ae3350ddf..92951a0dc 100644 --- a/vectordb_bench/backend/clients/elastic_cloud/elastic_cloud.py +++ b/vectordb_bench/backend/clients/elastic_cloud/elastic_cloud.py @@ -6,9 +6,10 @@ from elasticsearch.helpers import bulk from vectordb_bench.backend.filter import Filter, FilterOp +from vectordb_bench.backend.payload import PayloadProfile from ..api import VectorDB -from .config import ElasticCloudIndexConfig +from .config import ElasticCloudFtsConfig, ElasticCloudIndexConfig for logger in ("elasticsearch", "elastic_transport"): logging.getLogger(logger).setLevel(logging.WARNING) @@ -30,7 +31,7 @@ def __init__( self, dim: int, db_config: dict, - db_case_config: ElasticCloudIndexConfig, + db_case_config: ElasticCloudIndexConfig | ElasticCloudFtsConfig, indice: str = "vdb_bench_indice", # must be lowercase id_col_name: str = "id", label_col_name: str = "label", @@ -47,6 +48,11 @@ def __init__( self.label_col_name = label_col_name self.vector_col_name = vector_col_name self.with_scalar_labels = with_scalar_labels + self._is_fts = isinstance(db_case_config, ElasticCloudFtsConfig) + self.text_col_name = "text" + self.filter_id_col_name = "filter_id" + if self._is_fts: + self.id_col_name = "doc_id" from elasticsearch import Elasticsearch @@ -59,6 +65,13 @@ def __init__( client.indices.delete(index=self.indice) self._create_indice(client) + @classmethod + def supports_full_text_search(cls) -> bool: + return True + + def has_text_field(self) -> bool: + return bool(getattr(self, "_is_fts", False) and getattr(self, "text_col_name", None)) + @contextmanager def init(self) -> None: """connect to elasticsearch""" @@ -71,6 +84,18 @@ def init(self) -> None: del self.client def _create_indice(self, client: any) -> None: + if self._is_fts: + mappings = self.case_config.index_param() + index_settings = { + "number_of_shards": self.case_config.number_of_shards, + "number_of_replicas": self.case_config.number_of_replicas, + "refresh_interval": self.case_config.refresh_interval, + } + index_settings.update(self.case_config.similarity_settings()) + settings = {"index": index_settings} + client.indices.create(index=self.indice, mappings=mappings, settings=settings) + return + mappings = { "_source": {"excludes": [self.vector_col_name]}, "properties": { @@ -149,13 +174,64 @@ def insert_embeddings( log.warning(f"Failed to insert data: {self.indice} error: {e!s}") return (0, e) + def insert_documents( + self, + texts: Iterable[str], + doc_ids: list[str], + **kwargs, + ) -> tuple[int, Exception | None]: + if not getattr(self, "_is_fts", False): + msg = "ElasticCloud full-text insert requires ElasticCloudFtsConfig" + raise RuntimeError(msg) + assert self.client is not None, "should self.init() first" + docs = list(texts) + if len(docs) != len(doc_ids): + msg = f"Mismatch between texts ({len(docs)}) and doc_ids ({len(doc_ids)}) lengths" + raise ValueError(msg) + filter_ids = kwargs.get("filter_ids") + if filter_ids is not None and len(filter_ids) != len(docs): + msg = f"Mismatch between texts ({len(docs)}) and filter_ids ({len(filter_ids)}) lengths" + raise ValueError(msg) + + actions = [] + for i, doc in enumerate(docs): + source = { + self.id_col_name: str(doc_ids[i]), + self.text_col_name: doc, + } + if filter_ids is not None: + source[self.filter_id_col_name] = int(filter_ids[i]) + actions.append( + { + "_index": self.indice, + "_id": str(doc_ids[i]), + "_source": source, + } + ) + try: + result = bulk(self.client, actions) + return result[0], None + except Exception as e: + log.warning(f"Failed to insert FTS docs: {self.indice} error: {e!s}") + return 0, e + def prepare_filter(self, filters: Filter): self.routing_key = None + is_fts = getattr(self, "_is_fts", False) if filters.type == FilterOp.NonFilter: - self.filter = [] + self.filter = None if is_fts else [] elif filters.type == FilterOp.NumGE: - self.filter = {"range": {self.id_col_name: {"gt": filters.int_value}}} + if is_fts: + if getattr(filters, "int_field", None) != self.filter_id_col_name: + msg = f"ElasticCloud FTS filters only support int_field='{self.filter_id_col_name}'" + raise ValueError(msg) + self.filter = {"range": {self.filter_id_col_name: {"gte": filters.int_value}}} + else: + self.filter = {"range": {self.id_col_name: {"gte": filters.int_value}}} elif filters.type == FilterOp.StrEqual: + if is_fts: + msg = f"Not support Filter for ElasticCloud FTS - {filters}" + raise ValueError(msg) self.filter = {"term": {self.label_col_name: filters.label_value}} if self.case_config.use_routing: self.routing_key = filters.label_value @@ -203,6 +279,50 @@ def search_embedding( ) return [h["fields"][self.id_col_name][0] for h in res["hits"]["hits"]] + def search_documents( + self, + query: str, + k: int = 100, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + **kwargs, + ) -> list[str]: + if not getattr(self, "_is_fts", False): + msg = "ElasticCloud full-text search requires ElasticCloudFtsConfig" + raise RuntimeError(msg) + if not self.supports_document_payload_profile(payload_profile): + msg = f"ElasticCloud does not support document payload_profile={payload_profile.value}" + raise NotImplementedError(msg) + assert self.client is not None, "should self.init() first" + source = [self.text_col_name] if payload_profile == PayloadProfile.TEXT else False + filter_path = ["hits.hits._id", f"hits.hits.fields.{self.id_col_name}"] + if payload_profile == PayloadProfile.TEXT: + filter_path.append(f"hits.hits._source.{self.text_col_name}") + query_clause = {"match": {self.text_col_name: query}} + if getattr(self, "filter", None): + query_clause = {"bool": {"must": query_clause, "filter": self.filter}} + search_kwargs = { + "index": self.indice, + "query": query_clause, + "size": k, + "_source": source, + "docvalue_fields": [self.id_col_name], + "filter_path": filter_path, + } + if payload_profile != PayloadProfile.TEXT: + search_kwargs["stored_fields"] = "_none_" + res = self.client.search(**search_kwargs) + doc_ids = [] + for hit in res.get("hits", {}).get("hits", []): + if hit.get("_id") is not None: + doc_ids.append(str(hit["_id"])) + continue + + fields = hit.get("fields", {}) + values = fields.get(self.id_col_name, []) + if values: + doc_ids.append(str(values[0])) + return doc_ids + def optimize(self, data_size: int | None = None): """optimize will be called between insertion and search in performance cases.""" assert self.client is not None, "should self.init() first" diff --git a/vectordb_bench/backend/clients/elasticsearch_compatible.py b/vectordb_bench/backend/clients/elasticsearch_compatible.py new file mode 100644 index 000000000..6e040b3c6 --- /dev/null +++ b/vectordb_bench/backend/clients/elasticsearch_compatible.py @@ -0,0 +1,23 @@ +def build_fts_index_param(bm25_k1: float | None, bm25_b: float | None) -> dict: + text_mapping = {"type": "text"} + if bm25_k1 is not None or bm25_b is not None: + text_mapping["similarity"] = "vdbbench_bm25" + return { + "properties": { + "doc_id": {"type": "keyword"}, + "filter_id": {"type": "long"}, + "text": text_mapping, + }, + } + + +def build_bm25_similarity_settings(bm25_k1: float | None, bm25_b: float | None) -> dict: + if bm25_k1 is None and bm25_b is None: + return {} + + bm25_settings = {"type": "BM25"} + if bm25_k1 is not None: + bm25_settings["k1"] = bm25_k1 + if bm25_b is not None: + bm25_settings["b"] = bm25_b + return {"similarity": {"vdbbench_bm25": bm25_settings}} diff --git a/vectordb_bench/backend/clients/endee/cli.py b/vectordb_bench/backend/clients/endee/cli.py index fb6e3c37c..5c9f0c9d3 100644 --- a/vectordb_bench/backend/clients/endee/cli.py +++ b/vectordb_bench/backend/clients/endee/cli.py @@ -10,6 +10,7 @@ click_parameter_decorators_from_typed_dict, get_custom_case_config, parse_task_stages, + resolve_db_note, ) from vectordb_bench.models import ( CaseConfig, @@ -94,6 +95,7 @@ def Endee(**parameters): # Filter out None values before creating config params_for_nd = {k: v for k, v in parameters.items() if v is not None} + params_for_nd["note"] = resolve_db_note(parameters["note"], parameters["note_file"]) db_config = EndeeConfig(**params_for_nd) custom_case_config = get_custom_case_config(parameters) diff --git a/vectordb_bench/backend/clients/hologres/hologres.py b/vectordb_bench/backend/clients/hologres/hologres.py index 9fecc5c01..c396e23b0 100644 --- a/vectordb_bench/backend/clients/hologres/hologres.py +++ b/vectordb_bench/backend/clients/hologres/hologres.py @@ -2,6 +2,8 @@ import json import logging +import struct +import threading from collections.abc import Generator from contextlib import contextmanager from io import StringIO @@ -9,6 +11,8 @@ import psycopg from psycopg import Connection, Cursor, sql +from psycopg.adapt import Dumper +from psycopg.pq import Format from ..api import VectorDB from .config import HologresConfig, HologresIndexConfig @@ -16,11 +20,39 @@ log = logging.getLogger(__name__) +class HoloFloat4Array: + """Lightweight wrapper for float arrays - 1 object per query instead of 768 Float4 wrappers.""" + + __slots__ = ("data",) + + def __init__(self, data: list[float]): + self.data = data + + +class HoloFloat4ArrayDumper(Dumper): + """Custom Dumper that serializes float arrays to PostgreSQL binary float4[] format.""" + + format = Format.BINARY + oid = 1021 # PostgreSQL OID for float4[] (_float4) + + def dump(self, obj: HoloFloat4Array) -> bytes: + # PostgreSQL binary array format: + # Header: ndim(4) + has_null(4) + OID elemtype(4) + dim_size(4) + lower_bound(4) = 20 bytes + # For each element: itemlen(4) + float32(4) = 8 bytes per element + header = struct.pack(">iiIii", 1, 0, 700, len(obj.data), 1) + return header + b"".join(struct.pack(">if", 4, x) for x in obj.data) + + +# Register HoloFloat4ArrayDumper globally so it's available for all connections and cursors. +# This must happen at module load time, before any connections/cursors are created, +# because psycopg cursors snapshot the adapter map at creation time. +psycopg.adapters.register_dumper(HoloFloat4Array, HoloFloat4ArrayDumper) + + class Hologres(VectorDB): """Use psycopg instructions""" - conn: psycopg.Connection[Any] | None = None - cursor: psycopg.Cursor[Any] | None = None + thread_safe: bool = True # each thread gets its own connection via threading.local() _tg_name: str = "vdb_bench_tg_1" @@ -42,13 +74,20 @@ def __init__( self._primary_field = "id" self._vector_field = "embedding" - # construct basic units - self.conn, self.cursor = self._create_connection(**self.db_config) + # Thread-local storage for per-thread connections (thread-safe concurrent inserts) + self._local = threading.local() + self._all_conns: list[psycopg.Connection[Any]] = [] + self._conns_lock = threading.Lock() + + # Temporary connection for setup, closed at end of __init__ + conn, cursor = self._create_connection(**self.db_config) + self._local.conn = conn + self._local.cursor = cursor # create vector extension if self.case_config.is_proxima(): - self.cursor.execute("CREATE EXTENSION proxima;") - self.conn.commit() + cursor.execute("CREATE EXTENSION proxima;") + conn.commit() log.info(f"{self.name} config values: {self.db_config}\n{self.case_config}") if not any( @@ -70,10 +109,24 @@ def __init__( if self.case_config.create_index_before_load: self._create_index() - self.cursor.close() - self.conn.close() - self.cursor = None - self.conn = None + cursor.close() + conn.close() + self._local.cursor = None + self._local.conn = None + + def __getstate__(self): + # Exclude unpicklable threading objects; recreated in __setstate__ + state = self.__dict__.copy() + state.pop("_local", None) + state.pop("_conns_lock", None) + state.pop("_all_conns", None) + return state + + def __setstate__(self, state: dict) -> None: + self.__dict__.update(state) + self._local = threading.local() + self._conns_lock = threading.Lock() + self._all_conns = [] @staticmethod def _create_connection(**kwargs) -> tuple[Connection, Cursor]: @@ -86,51 +139,103 @@ def _create_connection(**kwargs) -> tuple[Connection, Cursor]: return conn, cursor + def _get_conn(self) -> Connection: + """Return this thread's connection, creating one if needed.""" + conn = getattr(self._local, "conn", None) + if conn is None or conn.closed: + conn, cursor = self._create_connection(**self.db_config) + self._local.conn = conn + self._local.cursor = cursor + self._set_search_guc_on(conn, cursor) + with self._conns_lock: + self._all_conns.append(conn) + return conn + + def _get_cursor(self) -> Cursor: + """Return this thread's cursor, creating connection if needed.""" + cursor = getattr(self._local, "cursor", None) + if cursor is None or cursor.closed: + self._get_conn() # creates both conn and cursor + cursor = self._local.cursor + return cursor + + def _set_search_guc_on(self, conn: Connection, cursor: Cursor) -> None: + """Set session-level search GUC on the given connection.""" + sql_guc = sql.SQL(f"SET hg_vector_ef_search = {self.case_config.ef_search};") + log.info(f"{self.name} client set search guc: {sql_guc.as_string()}") + cursor.execute(sql_guc) + conn.commit() + + def _set_search_guc(self) -> None: + """Set session-level search GUC on this thread's connection.""" + self._set_search_guc_on(self._get_conn(), self._get_cursor()) + @contextmanager def init(self) -> Generator[None, None, None]: - """ - Examples: - >>> with self.init(): - >>> self.insert_embeddings() - >>> self.search_embedding() - """ + """Open connection for calling thread, prepare for operations. - self.conn, self.cursor = self._create_connection(**self.db_config) + Worker threads lazily create their own connections via _get_conn(). + On exit, closes all thread-local connections. + """ + conn, cursor = self._create_connection(**self.db_config) + self._local.conn = conn + self._local.cursor = cursor + self._set_search_guc_on(conn, cursor) + with self._conns_lock: + self._all_conns.append(conn) + + self._search_query_no_filter = sql.SQL(""" + SELECT id + FROM {table_name} + ORDER BY {distance_function}(embedding, %b) + {order_direction} + LIMIT %s; + """).format( + table_name=sql.Identifier(self.table_name), + distance_function=sql.SQL(self.case_config.distance_function()), + order_direction=sql.SQL(self.case_config.order_direction()), + ) - self._set_search_guc() + self._search_query_with_filter = sql.SQL(""" + SELECT id + FROM {table_name} + WHERE id >= %s + ORDER BY {distance_function}(embedding, %b) + {order_direction} + LIMIT %s; + """).format( + table_name=sql.Identifier(self.table_name), + distance_function=sql.SQL(self.case_config.distance_function()), + order_direction=sql.SQL(self.case_config.order_direction()), + ) try: yield finally: - self.cursor.close() - self.conn.close() - self.cursor = None - self.conn = None - - def _set_search_guc(self): - assert self.conn is not None, "Connection is not initialized" - assert self.cursor is not None, "Cursor is not initialized" - - sql_guc = sql.SQL(f"SET hg_vector_ef_search = {self.case_config.ef_search};") - log.info(f"{self.name} client set search guc: {sql_guc.as_string()}") - self.cursor.execute(sql_guc) - self.conn.commit() + with self._conns_lock: + conns_to_close = list(self._all_conns) + self._all_conns.clear() + for c in conns_to_close: + if not c.closed: + c.close() + self._local.conn = None + self._local.cursor = None def _drop_table(self): - assert self.conn is not None, "Connection is not initialized" - assert self.cursor is not None, "Cursor is not initialized" + conn = self._get_conn() + cursor = self._get_cursor() log.info(f"{self.name} client drop table : {self.table_name}") - self.cursor.execute( + cursor.execute( sql.SQL("DROP TABLE IF EXISTS {table_name};").format( table_name=sql.Identifier(self.table_name), ), ) - self.conn.commit() + conn.commit() try: log.info(f"{self.name} client purge table recycle bin: {self.table_name}") - self.cursor.execute( + cursor.execute( sql.SQL("purge TABLE {table_name};").format( table_name=sql.Identifier(self.table_name), ), @@ -138,23 +243,23 @@ def _drop_table(self): except Exception as e: log.info(f"{self.name} client purge table {self.table_name} recycle bin failed, error: {e}, ignore.") finally: - self.conn.commit() + conn.commit() try: log.info(f"{self.name} client drop table group : {self._tg_name}") - self.cursor.execute(sql.SQL(f"CALL HG_DROP_TABLE_GROUP('{self._tg_name}');")) + cursor.execute(sql.SQL(f"CALL HG_DROP_TABLE_GROUP('{self._tg_name}');")) except Exception as e: log.info(f"{self.name} client drop table group : {self._tg_name} failed, error: {e}, ignore.") finally: - self.conn.commit() + conn.commit() try: log.info(f"{self.name} client free cache") - self.cursor.execute("select hg_admin_command('freecache');") + cursor.execute("select hg_admin_command('freecache');") except Exception as e: log.info(f"{self.name} client free cache failed, error: {e}, ignore.") finally: - self.conn.commit() + conn.commit() def optimize(self, data_size: int | None = None): if self.case_config.create_index_after_load: @@ -163,12 +268,13 @@ def optimize(self, data_size: int | None = None): self._analyze() def _vacuum(self): + conn = self._get_conn() log.info(f"{self.name} client vacuum table : {self.table_name}") try: # VACUUM cannot run inside a transaction block # it's better to new a connection - self.conn.autocommit = True - with self.conn.cursor() as cursor: + conn.autocommit = True + with conn.cursor() as cursor: cursor.execute( sql.SQL(""" VACUUM {table_name}; @@ -181,16 +287,18 @@ def _vacuum(self): log.warning(f"Failed to vacuum table: {self.table_name} error: {e}") raise e from None finally: - self.conn.autocommit = True + conn.autocommit = True def _analyze(self): + cursor = self._get_cursor() log.info(f"{self.name} client analyze table : {self.table_name}") - self.cursor.execute(sql.SQL(f"ANALYZE {self.table_name};")) + cursor.execute(sql.SQL(f"ANALYZE {self.table_name};")) log.info(f"{self.name} client analyze table : {self.table_name} done") def _full_compact(self): + cursor = self._get_cursor() log.info(f"{self.name} client full compact table : {self.table_name}") - self.cursor.execute( + cursor.execute( sql.SQL(""" SELECT hologres.hg_full_compact_table( '{table_name}', @@ -204,8 +312,8 @@ def _full_compact(self): log.info(f"{self.name} client full compact table : {self.table_name} done") def _create_index(self): - assert self.conn is not None, "Connection is not initialized" - assert self.cursor is not None, "Cursor is not initialized" + conn = self._get_conn() + cursor = self._get_cursor() sql_index = sql.SQL(""" CALL set_table_property ('{table_name}', 'vectors', '{{ @@ -224,15 +332,15 @@ def _create_index(self): log.info(f"{self.name} client create index on table : {self.table_name}, with sql: {sql_index.as_string()}") try: - self.cursor.execute(sql_index) - self.conn.commit() + cursor.execute(sql_index) + conn.commit() except Exception as e: log.warning(f"Failed to create index on table: {self.table_name} error: {e}") raise e from None def _set_replica_count(self, replica_count: int = 2): - assert self.conn is not None, "Connection is not initialized" - assert self.cursor is not None, "Cursor is not initialized" + conn = self._get_conn() + cursor = self._get_cursor() try: # non-warehouse mode by default @@ -243,13 +351,13 @@ def _set_replica_count(self, replica_count: int = 2): # check warehouse mode sql_check = sql.SQL("select count(*) from hologres.hg_warehouses;") log.info(f"check warehouse mode with sql: {sql_check}") - self.cursor.execute(sql_check) - result_check = self.cursor.fetchone()[0] + cursor.execute(sql_check) + result_check = cursor.fetchone()[0] if result_check > 0: # get warehouse name sql_get_warehouse_name = sql.SQL("select current_warehouse();") log.info(f"get warehouse name with sql: {sql_get_warehouse_name}") - self.cursor.execute(sql_get_warehouse_name) + cursor.execute(sql_get_warehouse_name) sql_tg_replica = sql.SQL(""" CALL hg_table_group_set_warehouse_replica_count ( '{dbname}.{tg_name}', @@ -258,29 +366,29 @@ def _set_replica_count(self, replica_count: int = 2): ); """).format( tg_name=sql.SQL(self._tg_name), - warehouse_name=sql.SQL(self.cursor.fetchone()[0]), + warehouse_name=sql.SQL(cursor.fetchone()[0]), dbname=sql.SQL(self.db_config["dbname"]), replica_count=replica_count, ) log.info(f"{self.name} client set table group replica: {self._tg_name}, with sql: {sql_tg_replica}") - self.cursor.execute(sql_tg_replica) + cursor.execute(sql_tg_replica) except Exception as e: log.warning(f"Failed to set replica count, error: {e}, ignore") finally: - self.conn.commit() + conn.commit() def _create_table(self, dim: int): - assert self.conn is not None, "Connection is not initialized" - assert self.cursor is not None, "Cursor is not initialized" + conn = self._get_conn() + cursor = self._get_cursor() sql_tg = sql.SQL(f"CALL HG_CREATE_TABLE_GROUP ('{self._tg_name}', 1);") log.info(f"{self.name} client create table group : {self._tg_name}, with sql: {sql_tg}") try: - self.cursor.execute(sql_tg) + cursor.execute(sql_tg) except Exception as e: log.warning(f"Failed to create table group : {self._tg_name} error: {e}, ignore") finally: - self.conn.commit() + conn.commit() self._set_replica_count(replica_count=2) @@ -297,8 +405,8 @@ def _create_table(self, dim: int): ) log.info(f"{self.name} client create table : {self.table_name}, with sql: {sql_table.as_string()}") try: - self.cursor.execute(sql_table) - self.conn.commit() + cursor.execute(sql_table) + conn.commit() except Exception as e: log.warning(f"Failed to create table : {self.table_name} error: {e}") raise e from None @@ -309,8 +417,8 @@ def insert_embeddings( metadata: list[int], **kwargs: Any, ) -> tuple[int, Exception | None]: - assert self.conn is not None, "Connection is not initialized" - assert self.cursor is not None, "Cursor is not initialized" + conn = self._get_conn() + cursor = self._get_cursor() try: buffer = StringIO() @@ -318,45 +426,17 @@ def insert_embeddings( buffer.write("%d\t%s\n" % (metadata[i], "{" + ",".join("%f" % x for x in embeddings[i]) + "}")) buffer.seek(0) - with self.cursor.copy( + with cursor.copy( sql.SQL("COPY {table_name} FROM STDIN").format(table_name=sql.Identifier(self.table_name)) ) as copy: copy.write(buffer.getvalue()) - self.conn.commit() + conn.commit() return len(metadata), None except Exception as e: log.warning(f"Failed to insert data into table ({self.table_name}), error: {e}") return 0, e - def _compose_query_and_params(self, vec: list[float], topk: int, ge_id: int | None = None): - params = [] - - where_clause = sql.SQL("") - if ge_id is not None: - where_clause = sql.SQL(" WHERE id >= %s ") - params.append(ge_id) - - vec_float4 = [psycopg._wrappers.Float4(i) for i in vec] - params.append(vec_float4) - params.append(topk) - - query = sql.SQL(""" - SELECT id - FROM {table_name} - {where_clause} - ORDER BY {distance_function}(embedding, %b) - {order_direction} - LIMIT %s; - """).format( - table_name=sql.Identifier(self.table_name), - distance_function=sql.SQL(self.case_config.distance_function()), - where_clause=where_clause, - order_direction=sql.SQL(self.case_config.order_direction()), - ) - - return query, params - def search_embedding( self, query: list[float], @@ -364,10 +444,16 @@ def search_embedding( filters: dict | None = None, timeout: int | None = None, ) -> list[int]: - assert self.conn is not None, "Connection is not initialized" - assert self.cursor is not None, "Cursor is not initialized" + cursor = self._get_cursor() ge = filters.get("id") if filters else None - q, params = self._compose_query_and_params(query, k, ge) - result = self.cursor.execute(q, params, prepare=True, binary=True) + q = HoloFloat4Array(query) + + if ge is not None: + params = (ge, q, k) + result = cursor.execute(self._search_query_with_filter, params, prepare=True, binary=True) + else: + params = (q, k) + result = cursor.execute(self._search_query_no_filter, params, prepare=True, binary=True) + return [int(i[0]) for i in result.fetchall()] diff --git a/vectordb_bench/backend/clients/lancedb/cli.py b/vectordb_bench/backend/clients/lancedb/cli.py index 219ae114e..494c28a2b 100644 --- a/vectordb_bench/backend/clients/lancedb/cli.py +++ b/vectordb_bench/backend/clients/lancedb/cli.py @@ -1,3 +1,4 @@ +import os from typing import Annotated, Unpack import click @@ -22,21 +23,134 @@ class LanceDBTypedDict(CommonTypedDict): str | None, click.option("--token", type=str, help="Authentication token", required=False), ] + cos_secret_id: Annotated[ + str | None, + click.option( + "--cos-secret-id", + type=str, + help="Tencent COS secret ID (or set COS_SECRET_ID env var)", + required=False, + ), + ] + cos_secret_key: Annotated[ + str | None, + click.option( + "--cos-secret-key", + type=str, + help="Tencent COS secret key (or set COS_SECRET_KEY env var)", + required=False, + ), + ] + cos_endpoint: Annotated[ + str | None, + click.option( + "--cos-endpoint", + type=str, + help="Tencent COS endpoint (or set COS_ENDPOINT env var)", + required=False, + ), + ] + cos_region: Annotated[ + str | None, + click.option( + "--cos-region", + type=str, + help="Tencent COS region (or set TENCENTCLOUD_REGION env var)", + required=False, + ), + ] + + +def _build_storage_options(**parameters) -> dict[str, str] | None: + """Build storage_options based on URI scheme. + + Supports: + - cos:// / s3:// → Tencent COS credentials + - goosefs:// → GooseFS authentication options + """ + uri = parameters.get("uri", "") + + # --- GooseFS storage options --- + if uri.startswith("goosefs://"): + return _build_goosefs_storage_options(**parameters) + + # --- COS / S3 storage options --- + if uri.startswith(("cos://", "s3://")): + return _build_cos_storage_options(**parameters) + + return None + + +def _build_cos_storage_options(**parameters) -> dict[str, str] | None: + """Build storage_options for COS/S3 if credentials are provided.""" + secret_id = parameters.get("cos_secret_id") or os.environ.get("COS_SECRET_ID") + secret_key = parameters.get("cos_secret_key") or os.environ.get("COS_SECRET_KEY") + endpoint = parameters.get("cos_endpoint") or os.environ.get("COS_ENDPOINT") + region = parameters.get("cos_region") or os.environ.get("TENCENTCLOUD_REGION") + + if not (secret_id and secret_key): + return None + + storage_options = { + "aws_access_key_id": secret_id, + "aws_secret_access_key": secret_key, + } + if endpoint: + storage_options["endpoint"] = endpoint + if region: + storage_options["region"] = region + + return storage_options + + +def _build_goosefs_storage_options(**_parameters: str) -> dict[str, str] | None: + """Build storage_options for GooseFS. + + Recognized environment variables: + - GOOSEFS_AUTH_TYPE → goosefs_auth_type (simple / nosasl) + - GOOSEFS_AUTH_USERNAME → goosefs_auth_username + - GOOSEFS_WRITE_TYPE → goosefs_write_type (CACHE_THROUGH etc.) + - GOOSEFS_BLOCK_SIZE → goosefs_block_size + - GOOSEFS_CHUNK_SIZE → goosefs_chunk_size + """ + storage_options: dict[str, str] = {} + + _goosefs_env_keys = { + "GOOSEFS_AUTH_TYPE": "goosefs_auth_type", + "GOOSEFS_AUTH_USERNAME": "goosefs_auth_username", + "GOOSEFS_WRITE_TYPE": "goosefs_write_type", + "GOOSEFS_BLOCK_SIZE": "goosefs_block_size", + "GOOSEFS_CHUNK_SIZE": "goosefs_chunk_size", + } + + for env_key, opt_key in _goosefs_env_keys.items(): + value = os.environ.get(env_key) + if value: + storage_options[opt_key] = value + + return storage_options if storage_options else None + + +def _build_db_config(**parameters): + from .config import LanceDBConfig + + return LanceDBConfig( + db_label=parameters["db_label"], + uri=parameters["uri"], + token=SecretStr(parameters["token"]) if parameters.get("token") else None, + storage_options=_build_storage_options(**parameters), + ) @cli.command() @click_parameter_decorators_from_typed_dict(LanceDBTypedDict) def LanceDB(**parameters: Unpack[LanceDBTypedDict]): - from .config import LanceDBConfig, _lancedb_case_config + from .config import LanceDBNoIndexConfig run( db=DB.LanceDB, - db_config=LanceDBConfig( - db_label=parameters["db_label"], - uri=parameters["uri"], - token=SecretStr(parameters["token"]) if parameters.get("token") else None, - ), - db_case_config=_lancedb_case_config.get("NONE")(), + db_config=_build_db_config(**parameters), + db_case_config=LanceDBNoIndexConfig(), **parameters, ) @@ -44,16 +158,12 @@ def LanceDB(**parameters: Unpack[LanceDBTypedDict]): @cli.command() @click_parameter_decorators_from_typed_dict(LanceDBTypedDict) def LanceDBAutoIndex(**parameters: Unpack[LanceDBTypedDict]): - from .config import LanceDBConfig, _lancedb_case_config + from .config import LanceDBAutoIndexConfig run( db=DB.LanceDB, - db_config=LanceDBConfig( - db_label=parameters["db_label"], - uri=parameters["uri"], - token=SecretStr(parameters["token"]) if parameters.get("token") else None, - ), - db_case_config=_lancedb_case_config.get(IndexType.AUTOINDEX)(), + db_config=_build_db_config(**parameters), + db_case_config=LanceDBAutoIndexConfig(), **parameters, ) @@ -65,7 +175,8 @@ class LanceDBIVFPQTypedDict(CommonTypedDict, LanceDBTypedDict): "--num-partitions", type=int, default=0, - help="Number of partitions for IVFPQ index, unset = use LanceDB default", + help="Number of partitions for IVF_PQ index, 0 = use LanceDB default", + show_default=True, ), ] num_sub_vectors: Annotated[ @@ -74,7 +185,8 @@ class LanceDBIVFPQTypedDict(CommonTypedDict, LanceDBTypedDict): "--num-sub-vectors", type=int, default=0, - help="Number of sub-vectors for IVFPQ index, unset = use LanceDB default", + help="Number of sub-vectors for IVF_PQ index, 0 = use LanceDB default", + show_default=True, ), ] nbits: Annotated[ @@ -83,13 +195,28 @@ class LanceDBIVFPQTypedDict(CommonTypedDict, LanceDBTypedDict): "--nbits", type=int, default=8, - help="Number of bits for IVFPQ index (must be 4 or 8), unset = use LanceDB default", + help="Number of bits for quantization (4 or 8)", + show_default=True, ), ] nprobes: Annotated[ int, click.option( - "--nprobes", type=int, default=0, help="Number of probes for IVFPQ search, unset = use LanceDB default" + "--nprobes", + type=int, + default=0, + help="Number of probes for IVF search, 0 = use LanceDB default", + show_default=True, + ), + ] + refine_factor: Annotated[ + int, + click.option( + "--refine-factor", + type=int, + default=0, + help="Refine factor for better recall, 0 = disabled", + show_default=True, ), ] @@ -97,50 +224,171 @@ class LanceDBIVFPQTypedDict(CommonTypedDict, LanceDBTypedDict): @cli.command() @click_parameter_decorators_from_typed_dict(LanceDBIVFPQTypedDict) def LanceDBIVFPQ(**parameters: Unpack[LanceDBIVFPQTypedDict]): - from .config import LanceDBConfig, LanceDBIndexConfig + from .config import LanceDBIndexConfig run( db=DB.LanceDB, - db_config=LanceDBConfig( - db_label=parameters["db_label"], - uri=parameters["uri"], - token=SecretStr(parameters["token"]) if parameters.get("token") else None, - ), + db_config=_build_db_config(**parameters), db_case_config=LanceDBIndexConfig( index=IndexType.IVFPQ, num_partitions=parameters["num_partitions"], num_sub_vectors=parameters["num_sub_vectors"], nbits=parameters["nbits"], nprobes=parameters["nprobes"], + refine_factor=parameters["refine_factor"], ), **parameters, ) -class LanceDBHNSWTypedDict(CommonTypedDict, LanceDBTypedDict): - m: Annotated[int, click.option("--m", type=int, default=0, help="HNSW parameter m")] +class LanceDBIVFHNSWSQTypedDict(CommonTypedDict, LanceDBTypedDict): + num_partitions: Annotated[ + int, + click.option( + "--num-partitions", + type=int, + default=0, + help="Number of IVF partitions, 0 = use LanceDB default", + show_default=True, + ), + ] + m: Annotated[ + int, + click.option("--m", type=int, default=0, help="HNSW parameter m, 0 = use LanceDB default", show_default=True), + ] ef_construction: Annotated[ - int, click.option("--ef-construction", type=int, default=0, help="HNSW parameter ef_construction") + int, + click.option( + "--ef-construction", + type=int, + default=0, + help="HNSW ef_construction, 0 = use LanceDB default", + show_default=True, + ), + ] + ef: Annotated[ + int, + click.option("--ef", type=int, default=0, help="HNSW search ef, 0 = use LanceDB default", show_default=True), + ] + nprobes: Annotated[ + int, + click.option( + "--nprobes", + type=int, + default=0, + help="Number of probes for IVF search, 0 = use LanceDB default", + show_default=True, + ), + ] + refine_factor: Annotated[ + int, + click.option( + "--refine-factor", + type=int, + default=0, + help="Refine factor for better recall, 0 = disabled", + show_default=True, + ), ] - ef: Annotated[int, click.option("--ef", type=int, default=0, help="HNSW search parameter ef")] @cli.command() -@click_parameter_decorators_from_typed_dict(LanceDBHNSWTypedDict) -def LanceDBHNSW(**parameters: Unpack[LanceDBHNSWTypedDict]): - from .config import LanceDBConfig, LanceDBHNSWIndexConfig +@click_parameter_decorators_from_typed_dict(LanceDBIVFHNSWSQTypedDict) +def LanceDBIVFHNSWSQ(**parameters: Unpack[LanceDBIVFHNSWSQTypedDict]): + from .config import LanceDBIVFHNSWSQConfig run( db=DB.LanceDB, - db_config=LanceDBConfig( - db_label=parameters["db_label"], - uri=parameters["uri"], - token=SecretStr(parameters["token"]) if parameters.get("token") else None, + db_config=_build_db_config(**parameters), + db_case_config=LanceDBIVFHNSWSQConfig( + num_partitions=parameters["num_partitions"], + m=parameters["m"], + ef_construction=parameters["ef_construction"], + ef=parameters["ef"], + nprobes=parameters["nprobes"], + refine_factor=parameters["refine_factor"], + ), + **parameters, + ) + + +class LanceDBIVFHNSWPQTypedDict(CommonTypedDict, LanceDBTypedDict): + num_partitions: Annotated[ + int, + click.option( + "--num-partitions", + type=int, + default=0, + help="Number of IVF partitions, 0 = use LanceDB default", + show_default=True, + ), + ] + num_sub_vectors: Annotated[ + int, + click.option( + "--num-sub-vectors", + type=int, + default=0, + help="Number of PQ sub-vectors, 0 = use LanceDB default", + show_default=True, ), - db_case_config=LanceDBHNSWIndexConfig( + ] + m: Annotated[ + int, + click.option("--m", type=int, default=0, help="HNSW parameter m, 0 = use LanceDB default", show_default=True), + ] + ef_construction: Annotated[ + int, + click.option( + "--ef-construction", + type=int, + default=0, + help="HNSW ef_construction, 0 = use LanceDB default", + show_default=True, + ), + ] + ef: Annotated[ + int, + click.option("--ef", type=int, default=0, help="HNSW search ef, 0 = use LanceDB default", show_default=True), + ] + nprobes: Annotated[ + int, + click.option( + "--nprobes", + type=int, + default=0, + help="Number of probes for IVF search, 0 = use LanceDB default", + show_default=True, + ), + ] + refine_factor: Annotated[ + int, + click.option( + "--refine-factor", + type=int, + default=0, + help="Refine factor for better recall, 0 = disabled", + show_default=True, + ), + ] + + +@cli.command() +@click_parameter_decorators_from_typed_dict(LanceDBIVFHNSWPQTypedDict) +def LanceDBIVFHNSWPQ(**parameters: Unpack[LanceDBIVFHNSWPQTypedDict]): + from .config import LanceDBIVFHNSWPQConfig + + run( + db=DB.LanceDB, + db_config=_build_db_config(**parameters), + db_case_config=LanceDBIVFHNSWPQConfig( + num_partitions=parameters["num_partitions"], + num_sub_vectors=parameters["num_sub_vectors"], m=parameters["m"], ef_construction=parameters["ef_construction"], ef=parameters["ef"], + nprobes=parameters["nprobes"], + refine_factor=parameters["refine_factor"], ), **parameters, ) diff --git a/vectordb_bench/backend/clients/lancedb/config.py b/vectordb_bench/backend/clients/lancedb/config.py index b0621c22e..8a98c36f3 100644 --- a/vectordb_bench/backend/clients/lancedb/config.py +++ b/vectordb_bench/backend/clients/lancedb/config.py @@ -6,18 +6,22 @@ class LanceDBConfig(DBConfig): """LanceDB connection configuration.""" - db_label: str - uri: str + db_label: str = "" + uri: str = "/tmp/lancedb" token: SecretStr | None = None + storage_options: dict[str, str] | None = None def to_dict(self) -> dict: return { "uri": self.uri, "token": self.token.get_secret_value() if self.token else None, + "storage_options": self.storage_options, } class LanceDBIndexConfig(BaseModel, DBCaseConfig): + """Default IVF_PQ index configuration.""" + index: IndexType = IndexType.IVFPQ metric_type: MetricType = MetricType.L2 num_partitions: int = 0 @@ -26,91 +30,166 @@ class LanceDBIndexConfig(BaseModel, DBCaseConfig): sample_rate: int = 256 max_iterations: int = 50 nprobes: int = 0 + refine_factor: int = 0 + + def parse_metric(self) -> str: + if self.metric_type in (MetricType.L2, MetricType.COSINE): + return self.metric_type.value.lower() + if self.metric_type in (MetricType.IP, MetricType.DP): + return "dot" + msg = f"Metric type {self.metric_type} is not supported for LanceDB!" + raise ValueError(msg) def index_param(self) -> dict: - if self.index not in [ - IndexType.IVFPQ, - IndexType.HNSW, - IndexType.AUTOINDEX, - IndexType.NONE, - ]: - msg = f"Index type {self.index} is not supported for LanceDB!" - raise ValueError(msg) - - # See https://lancedb.github.io/lancedb/python/python/#lancedb.table.Table.create_index params = { "metric": self.parse_metric(), "num_bits": self.nbits, "sample_rate": self.sample_rate, "max_iterations": self.max_iterations, } - if self.num_partitions > 0: params["num_partitions"] = self.num_partitions if self.num_sub_vectors > 0: params["num_sub_vectors"] = self.num_sub_vectors - return params def search_param(self) -> dict: params = {} if self.nprobes > 0: params["nprobes"] = self.nprobes - + if self.refine_factor > 0: + params["refine_factor"] = self.refine_factor return params - def parse_metric(self) -> str: - if self.metric_type in [MetricType.L2, MetricType.COSINE]: - return self.metric_type.value.lower() - if self.metric_type in [MetricType.IP, MetricType.DP]: - return "dot" - msg = f"Metric type {self.metric_type} is not supported for LanceDB!" - raise ValueError(msg) - class LanceDBNoIndexConfig(LanceDBIndexConfig): + """No index — brute-force scan.""" + index: IndexType = IndexType.NONE def index_param(self) -> dict: return {} + def search_param(self) -> dict: + params = {} + if self.refine_factor > 0: + params["refine_factor"] = self.refine_factor + return params + class LanceDBAutoIndexConfig(LanceDBIndexConfig): + """AutoIndex — let LanceDB decide.""" + index: IndexType = IndexType.AUTOINDEX def index_param(self) -> dict: - return {} + return {"metric": self.parse_metric()} + def search_param(self) -> dict: + params = {} + if self.nprobes > 0: + params["nprobes"] = self.nprobes + if self.refine_factor > 0: + params["refine_factor"] = self.refine_factor + return params + + +class LanceDBIVFHNSWSQConfig(BaseModel, DBCaseConfig): + """IVF_HNSW_SQ index — IVF partitioning + HNSW graph + scalar quantization.""" -class LanceDBHNSWIndexConfig(LanceDBIndexConfig): - index: IndexType = IndexType.HNSW + index: IndexType = IndexType.IVF_HNSW_SQ + metric_type: MetricType = MetricType.L2 + num_partitions: int = 0 m: int = 0 ef_construction: int = 0 ef: int = 0 + nprobes: int = 0 + refine_factor: int = 0 - def index_param(self) -> dict: - params = LanceDBIndexConfig.index_param(self) + def parse_metric(self) -> str: + if self.metric_type in (MetricType.L2, MetricType.COSINE): + return self.metric_type.value.lower() + if self.metric_type in (MetricType.IP, MetricType.DP): + return "dot" + msg = f"Metric type {self.metric_type} is not supported for LanceDB!" + raise ValueError(msg) - # See https://lancedb.github.io/lancedb/python/python/#lancedb.index.HnswSq - params["index_type"] = "IVF_HNSW_SQ" + def index_param(self) -> dict: + params = { + "metric": self.parse_metric(), + "index_type": "IVF_HNSW_SQ", + } + if self.num_partitions > 0: + params["num_partitions"] = self.num_partitions if self.m > 0: params["m"] = self.m if self.ef_construction > 0: params["ef_construction"] = self.ef_construction - return params def search_param(self) -> dict: params = {} - if self.ef != 0: - params = {"ef": self.ef} + if self.ef > 0: + params["ef"] = self.ef + if self.nprobes > 0: + params["nprobes"] = self.nprobes + if self.refine_factor > 0: + params["refine_factor"] = self.refine_factor + return params + + +class LanceDBIVFHNSWPQConfig(BaseModel, DBCaseConfig): + """IVF_HNSW_PQ index — IVF partitioning + HNSW graph + product quantization.""" + index: IndexType = IndexType.IVF_HNSW_PQ + metric_type: MetricType = MetricType.L2 + num_partitions: int = 0 + num_sub_vectors: int = 0 + m: int = 0 + ef_construction: int = 0 + ef: int = 0 + nprobes: int = 0 + refine_factor: int = 0 + + def parse_metric(self) -> str: + if self.metric_type in (MetricType.L2, MetricType.COSINE): + return self.metric_type.value.lower() + if self.metric_type in (MetricType.IP, MetricType.DP): + return "dot" + msg = f"Metric type {self.metric_type} is not supported for LanceDB!" + raise ValueError(msg) + + def index_param(self) -> dict: + params = { + "metric": self.parse_metric(), + "index_type": "IVF_HNSW_PQ", + } + if self.num_partitions > 0: + params["num_partitions"] = self.num_partitions + if self.num_sub_vectors > 0: + params["num_sub_vectors"] = self.num_sub_vectors + if self.m > 0: + params["m"] = self.m + if self.ef_construction > 0: + params["ef_construction"] = self.ef_construction + return params + + def search_param(self) -> dict: + params = {} + if self.ef > 0: + params["ef"] = self.ef + if self.nprobes > 0: + params["nprobes"] = self.nprobes + if self.refine_factor > 0: + params["refine_factor"] = self.refine_factor return params _lancedb_case_config = { IndexType.IVFPQ: LanceDBIndexConfig, IndexType.AUTOINDEX: LanceDBAutoIndexConfig, - IndexType.HNSW: LanceDBHNSWIndexConfig, + IndexType.IVF_HNSW_SQ: LanceDBIVFHNSWSQConfig, + IndexType.IVF_HNSW_PQ: LanceDBIVFHNSWPQConfig, + IndexType.HNSW: LanceDBIVFHNSWSQConfig, # backward compat: HNSW maps to IVF_HNSW_SQ IndexType.NONE: LanceDBNoIndexConfig, } diff --git a/vectordb_bench/backend/clients/lancedb/lancedb.py b/vectordb_bench/backend/clients/lancedb/lancedb.py index 65330e2cb..2febc2f3c 100644 --- a/vectordb_bench/backend/clients/lancedb/lancedb.py +++ b/vectordb_bench/backend/clients/lancedb/lancedb.py @@ -1,29 +1,44 @@ +"""Wrapper around the LanceDB vector database over VectorDB""" + import logging +import os from contextlib import contextmanager import lancedb import pyarrow as pa -from lancedb.pydantic import LanceModel + +from vectordb_bench.backend.filter import Filter, FilterOp from ..api import IndexType, VectorDB -from .config import LanceDBConfig, LanceDBIndexConfig +from .config import LanceDBIndexConfig log = logging.getLogger(__name__) - -class VectorModel(LanceModel): - id: int - vector: list[float] +# Rows per ``table.add`` call. Each call produces a new Lance data file +# (fragment), so enlarging this value directly controls the on-disk fragment +# size. Override via the ``LANCEDB_BATCH_SIZE`` environment variable. +# +# Rough sizing for float32 vectors: bytes_per_fragment ≈ rows * dim * 4. +# Example: dim=768, rows=170000 -> ~498 MB per fragment. +LANCEDB_BATCH_SIZE = int(os.environ.get("LANCEDB_BATCH_SIZE", "5000")) class LanceDB(VectorDB): + supported_filter_types: list[FilterOp] = [ + FilterOp.NonFilter, + FilterOp.NumGE, + FilterOp.StrEqual, + ] + thread_safe: bool = False + def __init__( self, dim: int, - db_config: LanceDBConfig, + db_config: dict, db_case_config: LanceDBIndexConfig, collection_name: str = "vector_bench_test", drop_old: bool = False, + with_scalar_labels: bool = False, **kwargs, ): self.name = "LanceDB" @@ -32,79 +47,206 @@ def __init__( self.table_name = collection_name self.dim = dim self.uri = db_config["uri"] - # avoid the search_param being called every time during the search process - self.search_config = db_case_config.search_param() + self.storage_options = db_config.get("storage_options") or None + self.with_scalar_labels = with_scalar_labels + self.where_clause = None + + self._id_field = "id" + self._vector_field = "vector" + self._label_field = "label" - log.info(f"Search config: {self.search_config}") + # cache search params to avoid repeated calls + self.search_config = db_case_config.search_param() + log.info(f"LanceDB search config: {self.search_config}") - db = lancedb.connect(self.uri) + connect_kwargs = {} + if self.storage_options: + connect_kwargs["storage_options"] = self.storage_options + db = lancedb.connect(self.uri, **connect_kwargs) if drop_old: try: db.drop_table(self.table_name) + log.info(f"LanceDB dropped old table: {self.table_name}") except Exception as e: log.warning(f"Failed to drop table {self.table_name}: {e}") - - try: - db.open_table(self.table_name) - except Exception: - schema = pa.schema( - [pa.field("id", pa.int64()), pa.field("vector", pa.list_(pa.float32(), list_size=self.dim))] - ) + # Always create a fresh table with the correct schema after drop. + # On remote storage (e.g. GooseFS) drop_table may not fully purge + # metadata immediately, causing open_table to succeed with a stale + # schema that is missing expected fields like 'id'. + schema = self._build_schema() db.create_table(self.table_name, schema=schema, mode="overwrite") + log.info(f"LanceDB created table: {self.table_name} (schema: {schema})") + else: + try: + db.open_table(self.table_name) + except Exception: + schema = self._build_schema() + db.create_table(self.table_name, schema=schema, mode="overwrite") + log.info(f"LanceDB created table: {self.table_name} (schema: {schema})") + + def _build_schema(self) -> pa.Schema: + fields = [ + pa.field(self._id_field, pa.int64()), + pa.field(self._vector_field, pa.list_(pa.float32(), list_size=self.dim)), + ] + if self.with_scalar_labels: + fields.append(pa.field(self._label_field, pa.utf8())) + return pa.schema(fields) @contextmanager def init(self): - self.db = lancedb.connect(self.uri) + connect_kwargs = {} + if self.storage_options: + connect_kwargs["storage_options"] = self.storage_options + self.db = lancedb.connect(self.uri, **connect_kwargs) self.table = self.db.open_table(self.table_name) yield self.db = None self.table = None + def __deepcopy__(self, memo: dict) -> "LanceDB": + """Custom deepcopy: skip live connection/table handles. + + The LanceDB ``Connection`` / ``Table`` objects wrap Rust bindings that + are not picklable. ``ConcurrentInsertRunner`` deep-copies the client + per thread for non-thread-safe DBs; the caller will then invoke + ``init()`` on the copy, which re-opens a fresh connection. + """ + cls = self.__class__ + new_obj = cls.__new__(cls) + memo[id(self)] = new_obj + from copy import deepcopy as _dc + + for k, v in self.__dict__.items(): + if k in ("db", "table"): + new_obj.__dict__[k] = None + else: + new_obj.__dict__[k] = _dc(v, memo) + return new_obj + def insert_embeddings( self, embeddings: list[list[float]], metadata: list[int], + labels_data: list[str] | None = None, **kwargs, ) -> tuple[int, Exception | None]: + assert self.table is not None, "Please call self.init() before" try: - data = [{"id": meta, "vector": emb} for meta, emb in zip(metadata, embeddings, strict=False)] - self.table.add(data) + log.info( + f"LanceDB insert_embeddings called with {len(embeddings)} rows, " + f"LANCEDB_BATCH_SIZE={LANCEDB_BATCH_SIZE} -> " + f"{(len(embeddings) + LANCEDB_BATCH_SIZE - 1) // LANCEDB_BATCH_SIZE} fragment(s)" + ) + for offset in range(0, len(embeddings), LANCEDB_BATCH_SIZE): + batch_emb = embeddings[offset : offset + LANCEDB_BATCH_SIZE] + batch_ids = metadata[offset : offset + LANCEDB_BATCH_SIZE] + + id_arr = pa.array(batch_ids, type=pa.int64()) + vec_arr = pa.FixedSizeListArray.from_arrays( + pa.array([v for emb in batch_emb for v in emb], type=pa.float32()), + list_size=self.dim, + ) + + if self.with_scalar_labels and labels_data is not None: + batch_labels = labels_data[offset : offset + LANCEDB_BATCH_SIZE] + label_arr = pa.array(batch_labels, type=pa.utf8()) + batch_table = pa.table( + { + self._id_field: id_arr, + self._vector_field: vec_arr, + self._label_field: label_arr, + } + ) + else: + batch_table = pa.table( + { + self._id_field: id_arr, + self._vector_field: vec_arr, + } + ) + self.table.add(batch_table) + return len(metadata), None except Exception as e: log.warning(f"Failed to insert data into LanceDB table ({self.table_name}), error: {e}") return 0, e + def prepare_filter(self, filters: Filter): + if filters.type == FilterOp.NonFilter: + self.where_clause = None + elif filters.type == FilterOp.NumGE: + self.where_clause = f"{self._id_field} >= {filters.int_value}" + elif filters.type == FilterOp.StrEqual: + self.where_clause = f"{self._label_field} = '{filters.label_value}'" + else: + msg = f"Unsupported filter for LanceDB: {filters}" + raise ValueError(msg) + def search_embedding( self, query: list[float], k: int = 100, - filters: dict | None = None, + **kwargs, ) -> list[int]: - if filters: - results = self.table.search(query).select(["id"]).where(f"id >= {filters['id']}", prefilter=True).limit(k) - if self.case_config.index == IndexType.IVFPQ and "nprobes" in self.search_config: - results = results.nprobes(self.search_config["nprobes"]).to_list() - elif self.case_config.index == IndexType.HNSW and "ef" in self.search_config: - results = results.ef(self.search_config["ef"]).to_list() - else: - results = results.to_list() - else: - results = self.table.search(query).select(["id"]).limit(k) - if self.case_config.index == IndexType.IVFPQ and "nprobes" in self.search_config: - results = results.nprobes(self.search_config["nprobes"]).to_list() - elif self.case_config.index == IndexType.HNSW and "ef" in self.search_config: - results = results.ef(self.search_config["ef"]).to_list() - else: - results = results.to_list() - - return [int(result["id"]) for result in results] + assert self.table is not None, "Please call self.init() before" + + # Include ``_distance`` in the projection to opt in to LanceDB's + # upcoming default behaviour. Without it, lance logs a per-query + # deprecation warning ("This search specified output columns but did + # not include `_distance`"). We only consume ``id`` downstream, so the + # extra column adds negligible overhead. + # + # Always pin the query metric to the case-configured metric. Otherwise + # the no-index path (brute-force scan) silently falls back to LanceDB's + # default (L2), which corrupts recall on cosine/IP datasets whose + # ground-truth is computed with a different metric. For indexed paths + # this is a no-op when the index metric already matches. + q = ( + self.table.search(query) + .metric(self.case_config.parse_metric()) + .select([self._id_field, "_distance"]) + .limit(k) + ) + + # apply filter + if self.where_clause: + q = q.where(self.where_clause, prefilter=True) + + # apply search parameters based on config + search_cfg = self.search_config + if "nprobes" in search_cfg: + q = q.nprobes(search_cfg["nprobes"]) + if "ef" in search_cfg: + q = q.ef(search_cfg["ef"]) + if "refine_factor" in search_cfg: + q = q.refine_factor(search_cfg["refine_factor"]) + + results = q.to_list() + return [int(r[self._id_field]) for r in results] def optimize(self, data_size: int | None = None): - if self.table and hasattr(self, "case_config") and self.case_config.index != IndexType.NONE: - log.info(f"Creating index for LanceDB table ({self.table_name})") - log.info(f"Index parameters: {self.case_config.index_param()}") - self.table.create_index(**self.case_config.index_param()) - # Better recall with IVF_PQ (though still bad) but breaks HNSW: https://github.com/lancedb/lancedb/issues/2369 - if self.case_config.index in (IndexType.IVFPQ, IndexType.AUTOINDEX): + assert self.table is not None, "Please call self.init() before" + + # Build index if configured + if self.case_config.index != IndexType.NONE: + index_params = self.case_config.index_param() + log.info(f"LanceDB creating index on table ({self.table_name}), params: {index_params}") + self.table.create_index(**index_params) + + # Compact fragments and clean up old versions for better performance. + # Prefer the unified ``table.optimize()`` API (lancedb >= 0.10), which + # internally handles both compaction and version cleanup without + # requiring the optional ``pylance`` package. Fall back to the legacy + # split APIs only if ``optimize`` is unavailable. + try: + if hasattr(self.table, "optimize"): self.table.optimize() + log.info(f"LanceDB optimize completed for table ({self.table_name})") + else: + self.table.compact_files() + self.table.cleanup_old_versions() + log.info(f"LanceDB compact_files + cleanup_old_versions completed for table ({self.table_name})") + except Exception as e: + log.warning(f"LanceDB optimize failed (non-fatal): {e}") diff --git a/vectordb_bench/backend/clients/lindorm/cli.py b/vectordb_bench/backend/clients/lindorm/cli.py index df466e517..cd4f69ae6 100644 --- a/vectordb_bench/backend/clients/lindorm/cli.py +++ b/vectordb_bench/backend/clients/lindorm/cli.py @@ -34,7 +34,10 @@ class LindormTypedDict(CommonTypedDict): ] -class LindormHNSWTypedDict(CommonTypedDict, LindormTypedDict, HNSWFlavor3): ... +class LindormHNSWTypedDict(CommonTypedDict, LindormTypedDict, HNSWFlavor3): + reorder_factor: Annotated[ + int, click.option("--reorder-factor", type=int, help="reorder factor", required=False, default=2) + ] @cli.command() @@ -56,6 +59,7 @@ def LindormHNSW(**parameters: Unpack[LindormHNSWTypedDict]): efConstruction=parameters["ef_construction"], efSearch=parameters["ef_search"], filter_type=parameters["filter_type"], + reorder_factor=parameters["reorder_factor"], number_of_regions=parameters["number_of_regions"], ), **parameters, diff --git a/vectordb_bench/backend/clients/lindorm/config.py b/vectordb_bench/backend/clients/lindorm/config.py index 0e0d4aae6..f8976103d 100644 --- a/vectordb_bench/backend/clients/lindorm/config.py +++ b/vectordb_bench/backend/clients/lindorm/config.py @@ -48,6 +48,7 @@ class HNSWConfig(LindormIndexConfig, DBCaseConfig): efSearch: int | None = None filter_type: str | None = "efficient_filter" k_expand_scope: int | None = 1000 + reorder_factor: int | None = 2 def index_param(self, dim: int | None = None) -> dict: return { @@ -61,7 +62,7 @@ def index_param(self, dim: int | None = None) -> dict: } def search_param(self, do_filter: bool = False) -> dict: - search_ext_param = {"lvector": {"ef_search": str(self.efSearch)}} + search_ext_param = {"lvector": {"ef_search": str(self.efSearch), "reorder_factor": str(self.reorder_factor)}} if do_filter: search_ext_param["lvector"]["filter_type"] = self.filter_type if self.filter_type == "efficient_filter": @@ -80,7 +81,7 @@ class IVFPQConfig(LindormIndexConfig, DBCaseConfig): centroids_hnsw_efSearch: int | None = None filter_type: str | None = "efficient_filter" - reorder_factor: int | None = 10 + reorder_factor: int | None = 2 client_refactor: bool = False k_expand_scope: int | None = 1000 @@ -125,7 +126,7 @@ class IVFBQConfig(LindormIndexConfig, DBCaseConfig): centroids_hnsw_efSearch: int | None = None filter_type: str | None = "efficient_filter" - reorder_factor: int | None = 10 + reorder_factor: int | None = 2 client_refactor: bool = False k_expand_scope: int | None = 1000 diff --git a/vectordb_bench/backend/clients/mariadb/mariadb.py b/vectordb_bench/backend/clients/mariadb/mariadb.py index db3863c85..e6053a0d8 100644 --- a/vectordb_bench/backend/clients/mariadb/mariadb.py +++ b/vectordb_bench/backend/clients/mariadb/mariadb.py @@ -108,13 +108,13 @@ def init(self): self.cursor.execute(f"SET mhnsw_ef_search = {search_param['ef_search']}") self.cursor.execute("COMMIT") - self.insert_sql = f"INSERT INTO {self.db_name}.{self.table_name} (id, v) VALUES (%s, %s)" # noqa: S608 + self.insert_sql = f"INSERT INTO {self.db_name}.{self.table_name} (id, v) VALUES (%s, %s)" self.select_sql = ( - f"SELECT id FROM {self.db_name}.{self.table_name}" # noqa: S608 + f"SELECT id FROM {self.db_name}.{self.table_name}" f"ORDER by vec_distance_{search_param['metric_type']}(v, %s) LIMIT %d" ) self.select_sql_with_filter = ( - f"SELECT id FROM {self.db_name}.{self.table_name} WHERE id >= %d " # noqa: S608 + f"SELECT id FROM {self.db_name}.{self.table_name} WHERE id >= %d " f"ORDER by vec_distance_{search_param['metric_type']}(v, %s) LIMIT %d" ) diff --git a/vectordb_bench/backend/clients/milvus/cli.py b/vectordb_bench/backend/clients/milvus/cli.py index 2f2a286be..738248ac3 100644 --- a/vectordb_bench/backend/clients/milvus/cli.py +++ b/vectordb_bench/backend/clients/milvus/cli.py @@ -1,7 +1,7 @@ from typing import Annotated, TypedDict, Unpack import click -from pydantic import SecretStr +from pydantic import BaseModel, SecretStr from vectordb_bench.backend.clients import DB from vectordb_bench.cli.cli import ( @@ -16,6 +16,31 @@ DBTYPE = DB.Milvus +def _use_partition_key(parameters: dict) -> bool: + explicit = parameters.get("use_partition_key") + if explicit is not None: + return explicit + return parameters.get("case_type") == "CloudMultiTenantSearchCase" + + +def _with_partition_key(db_case_config: BaseModel, parameters: dict) -> BaseModel: + return db_case_config.model_copy(update={"use_partition_key": _use_partition_key(parameters)}) + + +def _build_milvus_config(parameters: dict) -> BaseModel: + from .config import MilvusConfig + + return MilvusConfig( + db_label=parameters["db_label"], + uri=SecretStr(parameters["uri"]), + user=parameters["user_name"], + password=SecretStr(parameters["password"]) if parameters["password"] else None, + num_shards=int(parameters["num_shards"]), + replica_number=int(parameters["replica_number"]), + collection_name=parameters["collection_name"], + ) + + class MilvusTypedDict(TypedDict): uri: Annotated[ str, @@ -51,6 +76,28 @@ class MilvusTypedDict(TypedDict): show_default=True, ), ] + collection_name: Annotated[ + str, + click.option( + "--collection-name", + type=str, + help="Collection name for Milvus", + required=False, + default="VDBBench", + show_default=True, + ), + ] + use_partition_key: Annotated[ + bool | None, + click.option( + "--use-partition-key/--no-use-partition-key", + default=None, + help=( + "Use the Milvus partition key on the label field. " + "Defaults to enabled for CloudMultiTenantSearchCase and disabled otherwise." + ), + ), + ] class MilvusAutoIndexTypedDict(CommonTypedDict, MilvusTypedDict): ... @@ -59,19 +106,12 @@ class MilvusAutoIndexTypedDict(CommonTypedDict, MilvusTypedDict): ... @cli.command() @click_parameter_decorators_from_typed_dict(MilvusAutoIndexTypedDict) def MilvusAutoIndex(**parameters: Unpack[MilvusAutoIndexTypedDict]): - from .config import AutoIndexConfig, MilvusConfig + from .config import AutoIndexConfig run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=AutoIndexConfig(), + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key(AutoIndexConfig(), parameters), **parameters, ) @@ -79,19 +119,12 @@ def MilvusAutoIndex(**parameters: Unpack[MilvusAutoIndexTypedDict]): @cli.command() @click_parameter_decorators_from_typed_dict(MilvusAutoIndexTypedDict) def MilvusFlat(**parameters: Unpack[MilvusAutoIndexTypedDict]): - from .config import FLATConfig, MilvusConfig + from .config import FLATConfig run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=FLATConfig(), + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key(FLATConfig(), parameters), **parameters, ) @@ -102,22 +135,18 @@ class MilvusHNSWTypedDict(CommonTypedDict, MilvusTypedDict, HNSWFlavor3): ... @cli.command() @click_parameter_decorators_from_typed_dict(MilvusHNSWTypedDict) def MilvusHNSW(**parameters: Unpack[MilvusHNSWTypedDict]): - from .config import HNSWConfig, MilvusConfig + from .config import HNSWConfig run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=HNSWConfig( - M=parameters["m"], - efConstruction=parameters["ef_construction"], - ef=parameters["ef_search"], + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + HNSWConfig( + M=parameters["m"], + efConstruction=parameters["ef_construction"], + ef=parameters["ef_search"], + ), + parameters, ), **parameters, ) @@ -167,26 +196,22 @@ class MilvusHNSWPQTypedDict(CommonTypedDict, MilvusTypedDict, MilvusHNSWTypedDic @cli.command() @click_parameter_decorators_from_typed_dict(MilvusHNSWPQTypedDict) def MilvusHNSWPQ(**parameters: Unpack[MilvusHNSWPQTypedDict]): - from .config import HNSWPQConfig, MilvusConfig + from .config import HNSWPQConfig run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=HNSWPQConfig( - M=parameters["m"], - efConstruction=parameters["ef_construction"], - ef=parameters["ef_search"], - nbits=parameters["nbits"], - refine=parameters["refine"], - refine_type=parameters["refine_type"], - refine_k=parameters["refine_k"], + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + HNSWPQConfig( + M=parameters["m"], + efConstruction=parameters["ef_construction"], + ef=parameters["ef_search"], + nbits=parameters["nbits"], + refine=parameters["refine"], + refine_type=parameters["refine_type"], + refine_k=parameters["refine_k"], + ), + parameters, ), **parameters, ) @@ -211,27 +236,23 @@ class MilvusHNSWPRQTypedDict( @cli.command() @click_parameter_decorators_from_typed_dict(MilvusHNSWPRQTypedDict) def MilvusHNSWPRQ(**parameters: Unpack[MilvusHNSWPRQTypedDict]): - from .config import HNSWPRQConfig, MilvusConfig + from .config import HNSWPRQConfig run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=HNSWPRQConfig( - M=parameters["m"], - efConstruction=parameters["ef_construction"], - ef=parameters["ef_search"], - nbits=parameters["nbits"], - refine=parameters["refine"], - refine_type=parameters["refine_type"], - refine_k=parameters["refine_k"], - nrq=parameters["nrq"], + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + HNSWPRQConfig( + M=parameters["m"], + efConstruction=parameters["ef_construction"], + ef=parameters["ef_search"], + nbits=parameters["nbits"], + refine=parameters["refine"], + refine_type=parameters["refine_type"], + refine_k=parameters["refine_k"], + nrq=parameters["nrq"], + ), + parameters, ), **parameters, ) @@ -252,26 +273,22 @@ class MilvusHNSWSQTypedDict(CommonTypedDict, MilvusTypedDict, MilvusHNSWTypedDic @cli.command() @click_parameter_decorators_from_typed_dict(MilvusHNSWSQTypedDict) def MilvusHNSWSQ(**parameters: Unpack[MilvusHNSWSQTypedDict]): - from .config import HNSWSQConfig, MilvusConfig + from .config import HNSWSQConfig run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=HNSWSQConfig( - M=parameters["m"], - efConstruction=parameters["ef_construction"], - ef=parameters["ef_search"], - sq_type=parameters["sq_type"], - refine=parameters["refine"], - refine_type=parameters["refine_type"], - refine_k=parameters["refine_k"], + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + HNSWSQConfig( + M=parameters["m"], + efConstruction=parameters["ef_construction"], + ef=parameters["ef_search"], + sq_type=parameters["sq_type"], + refine=parameters["refine"], + refine_type=parameters["refine_type"], + refine_k=parameters["refine_k"], + ), + parameters, ), **parameters, ) @@ -283,21 +300,17 @@ class MilvusIVFFlatTypedDict(CommonTypedDict, MilvusTypedDict, IVFFlatTypedDictN @cli.command() @click_parameter_decorators_from_typed_dict(MilvusIVFFlatTypedDict) def MilvusIVFFlat(**parameters: Unpack[MilvusIVFFlatTypedDict]): - from .config import IVFFlatConfig, MilvusConfig + from .config import IVFFlatConfig run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=IVFFlatConfig( - nlist=parameters["nlist"], - nprobe=parameters["nprobe"], + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + IVFFlatConfig( + nlist=parameters["nlist"], + nprobe=parameters["nprobe"], + ), + parameters, ), **parameters, ) @@ -306,21 +319,17 @@ def MilvusIVFFlat(**parameters: Unpack[MilvusIVFFlatTypedDict]): @cli.command() @click_parameter_decorators_from_typed_dict(MilvusIVFFlatTypedDict) def MilvusIVFSQ8(**parameters: Unpack[MilvusIVFFlatTypedDict]): - from .config import IVFSQ8Config, MilvusConfig + from .config import IVFSQ8Config run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=IVFSQ8Config( - nlist=parameters["nlist"], - nprobe=parameters["nprobe"], + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + IVFSQ8Config( + nlist=parameters["nlist"], + nprobe=parameters["nprobe"], + ), + parameters, ), **parameters, ) @@ -368,25 +377,21 @@ class MilvusIVFRABITQTypedDict(CommonTypedDict, MilvusTypedDict, MilvusIVFFlatTy @cli.command() @click_parameter_decorators_from_typed_dict(MilvusIVFRABITQTypedDict) def MilvusIVFRabitQ(**parameters: Unpack[MilvusIVFRABITQTypedDict]): - from .config import IVFRABITQConfig, MilvusConfig + from .config import IVFRABITQConfig run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=IVFRABITQConfig( - nlist=parameters["nlist"], - nprobe=parameters["nprobe"], - rbq_bits_query=parameters["rbq_bits_query"], - refine=parameters["refine"], - refine_type=parameters["refine_type"], - refine_k=parameters["refine_k"], + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + IVFRABITQConfig( + nlist=parameters["nlist"], + nprobe=parameters["nprobe"], + rbq_bits_query=parameters["rbq_bits_query"], + refine=parameters["refine"], + refine_type=parameters["refine_type"], + refine_k=parameters["refine_k"], + ), + parameters, ), **parameters, ) @@ -399,20 +404,16 @@ class MilvusDISKANNTypedDict(CommonTypedDict, MilvusTypedDict): @cli.command() @click_parameter_decorators_from_typed_dict(MilvusDISKANNTypedDict) def MilvusDISKANN(**parameters: Unpack[MilvusDISKANNTypedDict]): - from .config import DISKANNConfig, MilvusConfig + from .config import DISKANNConfig run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=DISKANNConfig( - search_list=parameters["search_list"], + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + DISKANNConfig( + search_list=parameters["search_list"], + ), + parameters, ), **parameters, ) @@ -429,23 +430,19 @@ class MilvusGPUIVFTypedDict(CommonTypedDict, MilvusTypedDict, MilvusIVFFlatTyped @cli.command() @click_parameter_decorators_from_typed_dict(MilvusGPUIVFTypedDict) def MilvusGPUIVFFlat(**parameters: Unpack[MilvusGPUIVFTypedDict]): - from .config import GPUIVFFlatConfig, MilvusConfig + from .config import GPUIVFFlatConfig run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=GPUIVFFlatConfig( - nlist=parameters["nlist"], - nprobe=parameters["nprobe"], - cache_dataset_on_device=parameters["cache_dataset_on_device"], - refine_ratio=parameters.get("refine_ratio"), + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + GPUIVFFlatConfig( + nlist=parameters["nlist"], + nprobe=parameters["nprobe"], + cache_dataset_on_device=parameters["cache_dataset_on_device"], + refine_ratio=parameters.get("refine_ratio"), + ), + parameters, ), **parameters, ) @@ -465,21 +462,168 @@ class MilvusGPUBruteForceTypedDict(CommonTypedDict, MilvusTypedDict): @cli.command() @click_parameter_decorators_from_typed_dict(MilvusGPUBruteForceTypedDict) def MilvusGPUBruteForce(**parameters: Unpack[MilvusGPUBruteForceTypedDict]): - from .config import GPUBruteForceConfig, MilvusConfig + from .config import GPUBruteForceConfig run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=GPUBruteForceConfig( - metric_type=parameters["metric_type"], - limit=parameters["limit"], # top-k for search + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + GPUBruteForceConfig( + metric_type=parameters["metric_type"], + limit=parameters["limit"], # top-k for search + ), + parameters, + ), + **parameters, + ) + + +class MilvusSVSVamanaTypedDict(CommonTypedDict, MilvusTypedDict): + svs_graph_max_degree: Annotated[ + int, + click.option( + "--svs-graph-max-degree", + type=int, + help="Maximum degree of the Vamana graph (4-256).", + required=True, + ), + ] + svs_construction_window_size: Annotated[ + int, + click.option( + "--svs-construction-window-size", + type=int, + help="Window size for graph construction.", + required=False, + default=40, + show_default=True, + ), + ] + svs_alpha: Annotated[ + float | None, + click.option( + "--svs-alpha", + type=float, + help="Pruning parameter (default: 1.2 for L2, 0.95 for IP/COSINE).", + required=False, + default=None, + ), + ] + svs_storage_kind: Annotated[ + str, + click.option( + "--svs-storage-kind", + type=click.Choice( + ["fp32", "fp16", "sqi8", "lvq4x0", "lvq4x4", "lvq4x8", "leanvec4x4", "leanvec4x8", "leanvec8x8"], + case_sensitive=False, + ), + help="Data storage format.", + required=False, + default="fp32", + show_default=True, + ), + ] + svs_search_window_size: Annotated[ + int | None, + click.option( + "--svs-search-window-size", + type=int, + help="Window size for search (1-10000).", + required=False, + default=None, + ), + ] + svs_search_buffer_capacity: Annotated[ + int | None, + click.option( + "--svs-search-buffer-capacity", + type=int, + help="Buffer capacity for search priority queue (1-10000).", + required=False, + default=None, + ), + ] + + +@cli.command() +@click_parameter_decorators_from_typed_dict(MilvusSVSVamanaTypedDict) +def MilvusSVSVamana(**parameters: Unpack[MilvusSVSVamanaTypedDict]): + from .config import SVSVamanaConfig + + run( + db=DBTYPE, + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + SVSVamanaConfig( + svs_graph_max_degree=parameters["svs_graph_max_degree"], + svs_construction_window_size=parameters["svs_construction_window_size"], + svs_alpha=parameters["svs_alpha"], + svs_storage_kind=parameters["svs_storage_kind"], + svs_search_window_size=parameters["svs_search_window_size"], + svs_search_buffer_capacity=parameters["svs_search_buffer_capacity"], + ), + parameters, + ), + **parameters, + ) + + +@cli.command() +@click_parameter_decorators_from_typed_dict(MilvusSVSVamanaTypedDict) +def MilvusSVSVamanaLVQ(**parameters: Unpack[MilvusSVSVamanaTypedDict]): + from .config import SVSVamanaLVQConfig + + run( + db=DBTYPE, + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + SVSVamanaLVQConfig( + svs_graph_max_degree=parameters["svs_graph_max_degree"], + svs_construction_window_size=parameters["svs_construction_window_size"], + svs_alpha=parameters["svs_alpha"], + svs_storage_kind=parameters["svs_storage_kind"], + svs_search_window_size=parameters["svs_search_window_size"], + svs_search_buffer_capacity=parameters["svs_search_buffer_capacity"], + ), + parameters, + ), + **parameters, + ) + + +class MilvusSVSVamanaLeanVecTypedDict(MilvusSVSVamanaTypedDict): + svs_leanvec_dim: Annotated[ + int, + click.option( + "--svs-leanvec-dim", + type=int, + help="Dimensionality for LeanVec compression (0 = d/2).", + required=False, + default=0, + show_default=True, + ), + ] + + +@cli.command() +@click_parameter_decorators_from_typed_dict(MilvusSVSVamanaLeanVecTypedDict) +def MilvusSVSVamanaLeanVec(**parameters: Unpack[MilvusSVSVamanaLeanVecTypedDict]): + from .config import SVSVamanaLeanVecConfig + + run( + db=DBTYPE, + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + SVSVamanaLeanVecConfig( + svs_graph_max_degree=parameters["svs_graph_max_degree"], + svs_construction_window_size=parameters["svs_construction_window_size"], + svs_alpha=parameters["svs_alpha"], + svs_storage_kind=parameters["svs_storage_kind"], + svs_search_window_size=parameters["svs_search_window_size"], + svs_search_buffer_capacity=parameters["svs_search_buffer_capacity"], + svs_leanvec_dim=parameters["svs_leanvec_dim"], + ), + parameters, ), **parameters, ) @@ -498,25 +642,21 @@ class MilvusGPUIVFPQTypedDict( @cli.command() @click_parameter_decorators_from_typed_dict(MilvusGPUIVFPQTypedDict) def MilvusGPUIVFPQ(**parameters: Unpack[MilvusGPUIVFPQTypedDict]): - from .config import GPUIVFPQConfig, MilvusConfig + from .config import GPUIVFPQConfig run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=GPUIVFPQConfig( - nlist=parameters["nlist"], - nprobe=parameters["nprobe"], - m=parameters["m"], - nbits=parameters["nbits"], - cache_dataset_on_device=parameters["cache_dataset_on_device"], - refine_ratio=parameters["refine_ratio"], + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + GPUIVFPQConfig( + nlist=parameters["nlist"], + nprobe=parameters["nprobe"], + m=parameters["m"], + nbits=parameters["nbits"], + cache_dataset_on_device=parameters["cache_dataset_on_device"], + refine_ratio=parameters["refine_ratio"], + ), + parameters, ), **parameters, ) @@ -539,29 +679,63 @@ class MilvusGPUCAGRATypedDict(CommonTypedDict, MilvusTypedDict, MilvusGPUIVFType @cli.command() @click_parameter_decorators_from_typed_dict(MilvusGPUCAGRATypedDict) def MilvusGPUCAGRA(**parameters: Unpack[MilvusGPUCAGRATypedDict]): - from .config import GPUCAGRAConfig, MilvusConfig + from .config import GPUCAGRAConfig + + run( + db=DBTYPE, + db_config=_build_milvus_config(parameters), + db_case_config=_with_partition_key( + GPUCAGRAConfig( + intermediate_graph_degree=parameters["intermediate_graph_degree"], + graph_degree=parameters["graph_degree"], + itopk_size=parameters["itopk_size"], + team_size=parameters["team_size"], + search_width=parameters["search_width"], + min_iterations=parameters["min_iterations"], + max_iterations=parameters["max_iterations"], + build_algo=parameters["build_algo"], + cache_dataset_on_device=parameters["cache_dataset_on_device"], + refine_ratio=parameters["refine_ratio"], + ), + parameters, + ), + **parameters, + ) + + +class MilvusFTSTypedDict(CommonTypedDict, MilvusTypedDict): + """TypedDict for Milvus FTS command parameters.""" + + drop_ratio_search: Annotated[ + float | None, + click.option( + "--drop-ratio-search", + type=float, + help="Drop ratio for search (optional, for performance tuning)", + required=False, + default=None, + ), + ] + + +@cli.command() +@click_parameter_decorators_from_typed_dict(MilvusFTSTypedDict) +def MilvusFTS(**parameters: Unpack[MilvusFTSTypedDict]): + """Run FTS (Full-Text Search) benchmark on Milvus using BM25. + + This command uses the MS MARCO dev/small dataset for FTS testing. + """ + from .config import MilvusFtsConfig + + # Set default case_type to large dataset if not specified + if parameters.get("case_type") == "Performance1536D50K": # Default from CommonTypedDict + parameters["case_type"] = "FTSBm25Performance" run( db=DBTYPE, - db_config=MilvusConfig( - db_label=parameters["db_label"], - uri=SecretStr(parameters["uri"]), - user=parameters["user_name"], - password=SecretStr(parameters["password"]) if parameters["password"] else None, - num_shards=int(parameters["num_shards"]), - replica_number=int(parameters["replica_number"]), - ), - db_case_config=GPUCAGRAConfig( - intermediate_graph_degree=parameters["intermediate_graph_degree"], - graph_degree=parameters["graph_degree"], - itopk_size=parameters["itopk_size"], - team_size=parameters["team_size"], - search_width=parameters["search_width"], - min_iterations=parameters["min_iterations"], - max_iterations=parameters["max_iterations"], - build_algo=parameters["build_algo"], - cache_dataset_on_device=parameters["cache_dataset_on_device"], - refine_ratio=parameters["refine_ratio"], + db_config=_build_milvus_config(parameters), + db_case_config=MilvusFtsConfig( + drop_ratio_search=parameters.get("drop_ratio_search"), ), **parameters, ) diff --git a/vectordb_bench/backend/clients/milvus/config.py b/vectordb_bench/backend/clients/milvus/config.py index b04901a95..99d5ac8cf 100644 --- a/vectordb_bench/backend/clients/milvus/config.py +++ b/vectordb_bench/backend/clients/milvus/config.py @@ -1,16 +1,19 @@ -from typing import Any +from typing import ClassVar -from pydantic import BaseModel, SecretStr, model_validator +from pydantic import BaseModel, SecretStr from ..api import DBCaseConfig, DBConfig, IndexType, MetricType, SQType class MilvusConfig(DBConfig): + _extra_empty_skip: ClassVar[frozenset[str]] = frozenset({"user", "password"}) + uri: SecretStr = "http://localhost:19530" user: str | None = None password: SecretStr | None = None num_shards: int = 1 replica_number: int = 1 + collection_name: str = "VDBBench" def to_dict(self) -> dict: return { @@ -19,21 +22,9 @@ def to_dict(self) -> dict: "password": self.password.get_secret_value() if self.password else None, "num_shards": self.num_shards, "replica_number": self.replica_number, + "collection_name": self.collection_name, } - @model_validator(mode="before") - @classmethod - def not_empty_field(cls, data: Any) -> Any: - if isinstance(data, dict): - skip = set(cls.common_short_configs()) | set(cls.common_long_configs()) | {"user", "password"} - for name, v in data.items(): - if name in skip: - continue - if isinstance(v, str) and len(v) == 0: - msg = f"Empty string for field '{name}'!" - raise ValueError(msg) - return data - class MilvusIndexConfig(BaseModel): """Base config for milvus""" @@ -444,8 +435,163 @@ def search_param(self) -> dict: } +class SVSVamanaConfig(MilvusIndexConfig, DBCaseConfig): + svs_graph_max_degree: int + svs_construction_window_size: int = 40 + svs_alpha: float | None = None + svs_storage_kind: str = "fp32" + svs_search_window_size: int | None = None + svs_search_buffer_capacity: int | None = None + index: IndexType = IndexType.SVS_VAMANA + + def index_param(self) -> dict: + params = { + "svs_graph_max_degree": self.svs_graph_max_degree, + "svs_construction_window_size": self.svs_construction_window_size, + "svs_storage_kind": self.svs_storage_kind, + } + if self.svs_alpha is not None: + params["svs_alpha"] = self.svs_alpha + return { + "metric_type": self.parse_metric(), + "index_type": self.index.value, + "params": params, + } + + def search_param(self) -> dict: + return { + "metric_type": self.parse_metric(), + "params": { + "svs_search_window_size": self.svs_search_window_size, + "svs_search_buffer_capacity": self.svs_search_buffer_capacity, + }, + } + + +class SVSVamanaLVQConfig(SVSVamanaConfig): + svs_storage_kind: str = "lvq4x4" + index: IndexType = IndexType.SVS_VAMANA_LVQ + + +class SVSVamanaLeanVecConfig(SVSVamanaConfig): + svs_storage_kind: str = "leanvec4x4" + svs_leanvec_dim: int = 0 + index: IndexType = IndexType.SVS_VAMANA_LEANVEC + + def index_param(self) -> dict: + params = { + "svs_graph_max_degree": self.svs_graph_max_degree, + "svs_construction_window_size": self.svs_construction_window_size, + "svs_storage_kind": self.svs_storage_kind, + "svs_leanvec_dim": self.svs_leanvec_dim, + } + if self.svs_alpha is not None: + params["svs_alpha"] = self.svs_alpha + return { + "metric_type": self.parse_metric(), + "index_type": self.index.value, + "params": params, + } + + +class MilvusFtsConfig(BaseModel, DBCaseConfig): + """ + 1. inverted_index_algo: Index algorithm selection + - "DAAT_MAXSCORE" (default): Suitable for high k values or queries with many terms, balanced performance + - "DAAT_WAND": Suitable for small k values or short queries, faster + - "TAAT_NAIVE": Dynamically adapts to collection changes (e.g., avgdl), but slower + 2. bm25_k1: BM25 term frequency saturation control [1.2, 2.0], default None + - None: Do not override Milvus product default + - Higher values: Increase importance of term frequency in document ranking + - Recommended range: 1.2-1.8, adjust based on query characteristics + 3. bm25_b: BM25 document length normalization control [0.0, 1.0], default None + - None: Do not override Milvus product default + - 1.0: No length normalization, longer documents have advantage + - 0.0: Full normalization, shorter documents have advantage + - 0.75: Balanced length normalization, commonly used default + 4. analyzer_tokenizer: Tokenizer type, default "standard" + - "standard": Standard tokenizer, suitable for English text + - "whitespace": Split by whitespace characters + - "keyword": No tokenization, preserve original text + 5. analyzer_enable_lowercase: Enable lowercase conversion, default True + - True: Convert all text to lowercase, improve matching rate + - False: Preserve original case + 6. analyzer_max_token_length: Maximum length of individual tokens, default 40 + - Limit length of overly long words + - Set to None to disable this limit + 7. analyzer_stop_words: Stop words list, default None + - Comma-separated stop words, e.g., "of,to,the,and,or" + - These words will be filtered out and not participate in indexing/search + 8. drop_ratio_search: Ratio of minimum values to ignore during search [0.0, 1.0], default None + - 0.0: Keep all values, highest recall + - 0.1-0.3: Improve search speed by 10-20%, slight impact on recall + """ + + index_type: str = "SPARSE_INVERTED_INDEX" + metric_type: MetricType = MetricType.BM25 + inverted_index_algo: str = "DAAT_MAXSCORE" # DAAT_MAXSCORE | DAAT_WAND | TAAT_NAIVE + bm25_k1: float | None = None + bm25_b: float | None = None + analyzer_tokenizer: str = "standard" + analyzer_enable_lowercase: bool = True + analyzer_max_token_length: int | None = None + analyzer_stop_words: str | None = None + drop_ratio_search: float | None = None + + def analyzer_param(self) -> dict: + analyzer_params = {} + if self.analyzer_tokenizer: + analyzer_params["tokenizer"] = self.analyzer_tokenizer + + filters = [] + if self.analyzer_enable_lowercase: + filters.append("lowercase") + + if self.analyzer_max_token_length: + filters.append({"type": "length", "max": self.analyzer_max_token_length}) + + if self.analyzer_stop_words: + stop_words = [word.strip() for word in self.analyzer_stop_words.split(",") if word.strip()] + if stop_words: + filters.append({"type": "stop", "stop_words": stop_words}) + + if filters: + analyzer_params["filter"] = filters + + return analyzer_params or {"tokenizer": "standard"} + + def sparse_index_param(self) -> dict: + params = { + "inverted_index_algo": self.inverted_index_algo, + } + if self.bm25_k1 is not None: + params["bm25_k1"] = self.bm25_k1 + if self.bm25_b is not None: + params["bm25_b"] = self.bm25_b + + return { + "index_type": self.index_type, + "metric_type": self.metric_type.value, + "params": params, + } + + def index_param(self) -> dict: + return {**self.sparse_index_param(), "analyzer_params": self.analyzer_param()} + + def search_param(self) -> dict: + + params: dict = {} + if self.drop_ratio_search is not None: + params["drop_ratio_search"] = self.drop_ratio_search + return { + "metric_type": self.metric_type.value, + "params": params, + } + + _milvus_case_config = { IndexType.AUTOINDEX: AutoIndexConfig, + IndexType.FTS: MilvusFtsConfig, IndexType.HNSW: HNSWConfig, IndexType.HNSW_SQ: HNSWSQConfig, IndexType.HNSW_PQ: HNSWPQConfig, @@ -461,4 +607,7 @@ def search_param(self) -> dict: IndexType.GPU_CAGRA: GPUCAGRAConfig, IndexType.GPU_BRUTE_FORCE: GPUBruteForceConfig, IndexType.SCANN_MILVUS: SCANNConfig, + IndexType.SVS_VAMANA: SVSVamanaConfig, + IndexType.SVS_VAMANA_LVQ: SVSVamanaLVQConfig, + IndexType.SVS_VAMANA_LEANVEC: SVSVamanaLeanVecConfig, } diff --git a/vectordb_bench/backend/clients/milvus/milvus.py b/vectordb_bench/backend/clients/milvus/milvus.py index 26257c7c4..73c3c242e 100644 --- a/vectordb_bench/backend/clients/milvus/milvus.py +++ b/vectordb_bench/backend/clients/milvus/milvus.py @@ -4,17 +4,23 @@ import time from collections.abc import Iterable from contextlib import contextmanager +from typing import Any -from pymilvus import Collection, CollectionSchema, DataType, FieldSchema, MilvusException, utility +from pymilvus import DataType, Function, FunctionType, MilvusClient, MilvusException from vectordb_bench.backend.filter import Filter, FilterOp +from vectordb_bench.backend.payload import PayloadProfile from ..api import VectorDB -from .config import MilvusIndexConfig +from .config import MilvusFtsConfig, MilvusIndexConfig log = logging.getLogger(__name__) MILVUS_LOAD_REQS_SIZE = 1.5 * 1024 * 1024 +MILVUS_FTS_BATCH_SIZE = 1000 +MILVUS_FORCE_MERGE_TARGET_SIZE_MB = ((1 << 63) - 1) // (1024**2) +MILVUS_FORCE_MERGE_MAX_ATTEMPTS = 10 +MILVUS_FORCE_MERGE_RETRY_INTERVAL_SECONDS = 30 class Milvus(VectorDB): @@ -24,15 +30,23 @@ class Milvus(VectorDB): FilterOp.StrEqual, ] - def __init__( + @classmethod + def supports_full_text_search(cls) -> bool: + return True + + def has_text_field(self) -> bool: + return bool(getattr(self, "_is_fts", False) and getattr(self, "_text_field", None)) + + def __init__( # noqa: PLR0915 self, dim: int, db_config: dict, - db_case_config: MilvusIndexConfig, + db_case_config: MilvusIndexConfig | MilvusFtsConfig, collection_name: str = "VDBBench", drop_old: bool = False, name: str = "Milvus", with_scalar_labels: bool = False, + fts_batch_size: int | None = None, **kwargs, ): """Initialize wrapper around the milvus vector database.""" @@ -40,87 +54,217 @@ def __init__( self.db_config = db_config self.case_config = db_case_config self.collection_name = collection_name - self.batch_size = int(MILVUS_LOAD_REQS_SIZE / (dim * 4)) self.with_scalar_labels = with_scalar_labels - self._primary_field = "pk" - self._scalar_id_field = "id" self._scalar_label_field = "label" - self._vector_field = "vector" - self._vector_index_name = "vector_idx" - self._scalar_id_index_name = "id_sort_idx" + self._scalar_payload_label_field = self._scalar_label_field + self._multitenant_partition_key_field = self._scalar_label_field self._scalar_labels_index_name = "labels_idx" + self._is_fts = isinstance(self.case_config, MilvusFtsConfig) + + if self._is_fts: + self.batch_size = fts_batch_size or MILVUS_FTS_BATCH_SIZE + self._primary_field = "doc_id" + self._text_field = "text" + self._filter_id_field = "filter_id" + self._sparse_field = "sparse_vector" + self._sparse_index_name = "sparse_vector_idx" + self._doc_id_sort_index_name = "doc_id_sort_idx" + self._filter_id_sort_index_name = "filter_id_sort_idx" + self._main_index_name = self._sparse_index_name + self._sort_index_name = self._doc_id_sort_index_name + self._sort_index_field = self._primary_field + else: + self.batch_size = int(MILVUS_LOAD_REQS_SIZE / (dim * 4)) + self._primary_field = "pk" + self._scalar_id_field = "id" + self._vector_field = "vector" + self._vector_index_name = "vector_idx" + self._scalar_id_index_name = "id_sort_idx" + self._main_index_name = self._vector_index_name + self._sort_index_name = self._scalar_id_index_name + self._sort_index_field = self._scalar_id_field + + self.multitenant_tenant_labels: list[str] = kwargs.get("multitenant_tenant_labels", []) + if self.multitenant_tenant_labels: + self._multitenant_partition_key_field = "labels" + if self.with_scalar_labels: + self._scalar_payload_label_field = "scalar_label" - from pymilvus import connections - - connections.connect( + client = MilvusClient( uri=self.db_config.get("uri"), user=self.db_config.get("user"), password=self.db_config.get("password"), + token=self.db_config.get("token", ""), timeout=30, ) - if drop_old and utility.has_collection(self.collection_name): + + if drop_old and client.has_collection(self.collection_name): log.info(f"{self.name} client drop_old collection: {self.collection_name}") - utility.drop_collection(self.collection_name) - - if not utility.has_collection(self.collection_name): - fields = [ - FieldSchema(self._primary_field, DataType.INT64, is_primary=True), - FieldSchema(self._scalar_id_field, DataType.INT64), - FieldSchema(self._vector_field, DataType.FLOAT_VECTOR, dim=dim), - ] - if self.with_scalar_labels: - is_partition_key = db_case_config.use_partition_key - log.info(f"with_scalar_labels, add a new varchar field, as partition_key: {is_partition_key}") - fields.append( - FieldSchema( - self._scalar_label_field, + client.drop_collection(self.collection_name) + + if not client.has_collection(self.collection_name): + schema = MilvusClient.create_schema() + if self._is_fts: + analyzer_params = ( + self.case_config.analyzer_param() + if hasattr(self.case_config, "analyzer_param") + else self.case_config.index_param().get("analyzer_params", {"type": "english"}) + ) + schema.add_field(self._primary_field, DataType.VARCHAR, max_length=512, is_primary=True) + schema.add_field(self._filter_id_field, DataType.INT64) + schema.add_field( + self._text_field, + DataType.VARCHAR, + max_length=65535, + enable_analyzer=True, + enable_match=True, + analyzer_params=analyzer_params, + ) + schema.add_field(self._sparse_field, DataType.SPARSE_FLOAT_VECTOR) + if self.with_scalar_labels: + schema.add_field(self._scalar_label_field, DataType.VARCHAR, max_length=256) + schema.add_function( + Function( + name="text_bm25_emb", + function_type=FunctionType.BM25, + input_field_names=[self._text_field], + output_field_names=[self._sparse_field], + params={}, + ) + ) + else: + schema.add_field(self._primary_field, DataType.INT64, is_primary=True) + schema.add_field(self._scalar_id_field, DataType.INT64) + schema.add_field(self._vector_field, DataType.FLOAT_VECTOR, dim=dim) + + if self.multitenant_tenant_labels: + schema.add_field( + self._multitenant_partition_key_field, DataType.VARCHAR, max_length=256, - is_partition_key=is_partition_key, + is_partition_key=True, ) - ) + + if self.with_scalar_labels: + is_partition_key = db_case_config.use_partition_key + log.info(f"with_scalar_labels, add a new varchar field, as partition_key: {is_partition_key}") + if not self.multitenant_tenant_labels or ( + self._scalar_payload_label_field != self._multitenant_partition_key_field + ): + schema.add_field( + self._scalar_payload_label_field, + DataType.VARCHAR, + max_length=256, + is_partition_key=is_partition_key and not self.multitenant_tenant_labels, + ) log.info(f"{self.name} create collection: {self.collection_name}") - # Create the collection - col = Collection( - name=self.collection_name, - schema=CollectionSchema(fields), - consistency_level="Session", + index_params = self._build_index_params() + client.create_collection( + collection_name=self.collection_name, + schema=schema, num_shards=self.db_config.get("num_shards", 1), + consistency_level="Session", + ) + client.create_index(self.collection_name, index_params) + client.load_collection( + self.collection_name, + replica_number=self.db_config.get("replica_number", 1), ) - self.create_index() - col.load(replica_number=self.db_config.get("replica_number", 1)) - - connections.disconnect("default") + client.close() - def create_index(self): - col = Collection(self.collection_name) - # vector index - col.create_index( - self._vector_field, - self.case_config.index_param(), - index_name=self._vector_index_name, - ) - # scalar index for range-expr (int-filter) - col.create_index( - self._scalar_id_field, - index_params={ - "index_type": "STL_SORT", - }, - index_name=self._scalar_id_index_name, + def _build_index_params(self): + index_params = MilvusClient.prepare_index_params() + if self._is_fts: + sparse_idx = ( + self.case_config.sparse_index_param() + if hasattr(self.case_config, "sparse_index_param") + else self.case_config.index_param() + ) + index_params.add_index( + field_name=self._sparse_field, + index_name=self._main_index_name, + index_type=sparse_idx.get("index_type", ""), + metric_type=sparse_idx.get("metric_type", ""), + params=sparse_idx.get("params", {}), + ) + else: + vec_idx = self.case_config.index_param() + index_params.add_index( + field_name=self._vector_field, + index_name=self._vector_index_name, + index_type=vec_idx.get("index_type", ""), + metric_type=vec_idx.get("metric_type", ""), + params=vec_idx.get("params", {}), + ) + index_params.add_index( + field_name=self._sort_index_field, + index_name=self._sort_index_name, + index_type="STL_SORT", ) - # scalar index for varchar (label-filter) + if self._is_fts: + index_params.add_index( + field_name=self._filter_id_field, + index_name=self._filter_id_sort_index_name, + index_type="STL_SORT", + ) if self.with_scalar_labels: - col.create_index( - self._scalar_label_field, - index_params={ - "index_type": "BITMAP", - }, + index_params.add_index( + field_name=self._scalar_payload_label_field, index_name=self._scalar_labels_index_name, + index_type="BITMAP", ) + return index_params + + def supports_multitenant(self) -> bool: + return True + + def validate_multitenant_schema(self) -> None: + client = MilvusClient( + uri=self.db_config.get("uri"), + user=self.db_config.get("user"), + password=self.db_config.get("password"), + token=self.db_config.get("token", ""), + timeout=30, + ) + try: + desc = client.describe_collection(self.collection_name) + fields = desc.get("fields", []) if isinstance(desc, dict) else [] + fields_by_name = {self._field_property(field, "name"): field for field in fields} + partition_key_field = self._find_multitenant_partition_key_field(fields_by_name) + if partition_key_field is None: + label_field = fields_by_name.get(self._scalar_label_field) + if label_field is None: + msg = f"{self.name} multitenant collection {self.collection_name} is missing tenant label field" + raise ValueError(msg) + msg = f"{self.name} multitenant collection {self.collection_name} label field is not a partition key" + raise ValueError(msg) + self._multitenant_partition_key_field = partition_key_field + if "scalar_label" in fields_by_name: + self._scalar_payload_label_field = "scalar_label" + finally: + client.close() + + def _find_multitenant_partition_key_field(self, fields_by_name: dict[str, dict | object]) -> str | None: + for field_name in [self._scalar_label_field, "labels"]: + field = fields_by_name.get(field_name) + if field is not None and self._field_property(field, "is_partition_key", False): + return field_name + return None + + @staticmethod + def _field_property(field: dict | object, name: str, default: Any = None): + if isinstance(field, dict): + if name in field: + return field[name] + params = field.get("params") + if isinstance(params, dict) and name in params: + return params[name] + return default + return getattr(field, name, default) @contextmanager def init(self): @@ -130,68 +274,121 @@ def init(self): >>> self.insert_embeddings() >>> self.search_embedding() """ - from pymilvus import connections + self.client: MilvusClient | None = None + self.client = MilvusClient( + uri=self.db_config.get("uri"), + user=self.db_config.get("user"), + password=self.db_config.get("password"), + token=self.db_config.get("token", ""), + timeout=60, + ) + yield + self.client.close() + self.client = None + + def _wait_for_segments_sorted(self): + while True: + segments = self.client.list_persistent_segments(self.collection_name) + unsorted = [s for s in segments if not s.is_sorted] + if not unsorted: + log.info(f"{self.name} all persistent segments are sorted.") + break + log.debug(f"{self.name} waiting for {len(unsorted)} segments to be sorted...") + time.sleep(5) + + def _wait_for_index(self): + while True: + info = self.client.describe_index(self.collection_name, self._main_index_name) + if info.get("pending_index_rows", -1) == 0: + break + time.sleep(5) + + def _wait_for_compaction_ready(self): + self._wait_for_segments_sorted() + self._wait_for_index() + + def _wait_for_compaction(self, compaction_id: int): + while True: + state = self.client.get_compaction_state(compaction_id) + if state == "Completed": + break + time.sleep(0.5) + + def _force_merge_source_segment_ids(self) -> set[int]: + # isCompacting is not exposed by MilvusClient, so verify plan coverage + # against every persistent data segment that force merge should consume. + segments = self.client.list_persistent_segments(self.collection_name) + return { + segment.segment_id + for segment in segments + if segment.state_name == "Flushed" and segment.level_name not in {"L0", "L2"} + } + + def _force_merge(self, max_attempts: int = MILVUS_FORCE_MERGE_MAX_ATTEMPTS): + if max_attempts <= 0: + message = "force merge max_attempts must be greater than zero" + raise ValueError(message) + + for attempt in range(1, max_attempts + 1): + self._wait_for_compaction_ready() + expected_source_ids = self._force_merge_source_segment_ids() + compaction_id = self.client.compact(self.collection_name, target_size=MILVUS_FORCE_MERGE_TARGET_SIZE_MB) + if compaction_id <= 0: + failure_detail = f"generated no plan for snapshot {sorted(expected_source_ids)}" + else: + self._wait_for_compaction(compaction_id) + plans = self.client.get_compaction_plans(compaction_id) + actual_source_ids = {source_id for plan in plans.plans for source_id in plan.sources} + attempt_missing_source_ids = expected_source_ids - actual_source_ids + if not attempt_missing_source_ids: + return - self.col: Collection | None = None + failure_detail = f"missing segments: {sorted(attempt_missing_source_ids)}" - connections.connect(**self.db_config, timeout=60) - # Grab the existing colection with connections - self.col = Collection(self.collection_name) + if attempt == max_attempts: + message = f"{self.name} force merge failed after {max_attempts} attempts; {failure_detail}" + raise RuntimeError(message) - yield - connections.disconnect("default") + log.info(f"{self.name} force merge attempt {attempt}/{max_attempts} {failure_detail}; retrying...") + time.sleep(MILVUS_FORCE_MERGE_RETRY_INTERVAL_SECONDS) def _optimize(self): log.info(f"{self.name} optimizing before search") - self._post_insert() - try: - self.col.load(refresh=True) - except Exception as e: - log.warning(f"{self.name} optimize error: {e}") - raise e from None - - def _post_insert(self): try: - self.col.flush() - # wait for index done and load refresh - self.create_index() - - utility.wait_for_index_building_complete(self.collection_name, index_name=self._vector_index_name) - - def wait_index(): - while True: - progress = utility.index_building_progress(self.collection_name, index_name=self._vector_index_name) - if progress.get("pending_index_rows", -1) == 0: - break - time.sleep(5) - - wait_index() + self.client.flush(self.collection_name) - # Skip compaction if use GPU indexType - if self.case_config.is_gpu_index: - log.debug("skip compaction for gpu index type.") + if getattr(self.case_config, "is_gpu_index", False): + log.debug("skip force merge compaction for gpu index type.") else: try: - self.col.compact() - self.col.wait_for_compaction_completed() - log.info("compaction completed. waiting for the rest of index building.") + self._wait_for_compaction_ready() + compaction_id = self.client.compact(self.collection_name) + if compaction_id > 0: + self._wait_for_compaction(compaction_id) + self._force_merge() + log.info(f"{self.name} force merge compaction completed.") except Exception as e: - log.warning(f"{self.name} compact error: {e}") - if hasattr(e, "code") and e.code().name == "PERMISSION_DENIED": - log.warning("Skip compact due to permission denied.") + log.warning(f"{self.name} compact or list segments error: {e}") + if getattr(getattr(e, "code", None), "name", None) == "PERMISSION_DENIED": + log.warning("Skip compact due to list segments or compact permission denied.") else: raise e from None - wait_index() + + # wait for index no matter what + self._wait_for_index() + self.client.refresh_load(self.collection_name) except Exception as e: log.warning(f"{self.name} optimize error: {e}") raise e from None def optimize(self, data_size: int | None = None): - assert self.col, "Please call self.init() before" + assert self.client, "Please call self.init() before" self._optimize() def need_normalize_cosine(self) -> bool: """Wheather this database need to normalize dataset to support COSINE""" + if self._is_fts: + return False if self.case_config.is_gpu_index: log.info("current gpu_index only supports IP / L2, cosine dataset need normalize.") return True @@ -203,58 +400,201 @@ def insert_embeddings( embeddings: Iterable[list[float]], metadata: list[int], labels_data: list[str] | None = None, + tenant_labels_data: list[str] | None = None, **kwargs, ) -> tuple[int, Exception]: """Insert embeddings into Milvus. should call self.init() first""" - # use the first insert_embeddings to init collection - assert self.col is not None + assert self.client is not None assert len(embeddings) == len(metadata) insert_count = 0 try: for batch_start_offset in range(0, len(embeddings), self.batch_size): batch_end_offset = min(batch_start_offset + self.batch_size, len(embeddings)) - insert_data = [ - metadata[batch_start_offset:batch_end_offset], - metadata[batch_start_offset:batch_end_offset], - embeddings[batch_start_offset:batch_end_offset], - ] - if self.with_scalar_labels: - insert_data.append(labels_data[batch_start_offset:batch_end_offset]) - res = self.col.insert(insert_data) - insert_count += len(res.primary_keys) + batch_data = [] + for i in range(batch_start_offset, batch_end_offset): + row = { + self._primary_field: metadata[i], + self._scalar_id_field: metadata[i], + self._vector_field: embeddings[i], + } + if tenant_labels_data is not None: + row[self._multitenant_partition_key_field] = tenant_labels_data[i] + if self.with_scalar_labels: + row[self._scalar_payload_label_field] = labels_data[i] + batch_data.append(row) + res = self.client.insert(self.collection_name, batch_data) + insert_count += res["insert_count"] except MilvusException as e: log.info(f"Failed to insert data: {e}") return insert_count, e return insert_count, None + def insert_documents( + self, + texts: Iterable[str], + doc_ids: list[str], + **kwargs, + ) -> tuple[int, Exception | None]: + """Insert documents into a Milvus BM25 full-text collection.""" + if not self._is_fts: + msg = "insert_documents is only valid in FTS mode" + raise RuntimeError(msg) + assert self.client is not None + + docs = list(texts) + if len(docs) != len(doc_ids): + msg = f"Mismatch between texts ({len(docs)}) and doc_ids ({len(doc_ids)}) lengths" + raise ValueError(msg) + + batch_size = kwargs.get("batch_size", self.batch_size) + labels_data = kwargs.get("labels_data") + filter_ids = kwargs.get("filter_ids") + if filter_ids is not None and len(filter_ids) != len(docs): + msg = f"Mismatch between texts ({len(docs)}) and filter_ids ({len(filter_ids)}) lengths" + raise ValueError(msg) + + insert_count = 0 + try: + for batch_start_offset in range(0, len(docs), batch_size): + batch_end_offset = min(batch_start_offset + batch_size, len(docs)) + rows = [] + for i in range(batch_start_offset, batch_end_offset): + row = { + self._primary_field: str(doc_ids[i]), + self._text_field: docs[i], + } + if filter_ids is not None: + row[self._filter_id_field] = int(filter_ids[i]) + if self.with_scalar_labels: + row[self._scalar_label_field] = labels_data[i] if labels_data is not None else "" + rows.append(row) + + res = self.client.insert(self.collection_name, rows) + insert_count += res["insert_count"] + if batch_start_offset // batch_size % 10 == 0: + log.debug( + f"{self.name} batch insert progress: {batch_end_offset}/{len(docs)} " + f"({batch_end_offset / len(docs) * 100:.1f}%)" + ) + except MilvusException as e: + log.info(f"{self.name} insert error: {e}") + return insert_count, e + return insert_count, None + def prepare_filter(self, filters: Filter): + if self._is_fts: + if filters.type == FilterOp.NonFilter: + self.expr = "" + return + if filters.type == FilterOp.NumGE: + if getattr(filters, "int_field", None) != self._filter_id_field: + msg = f"Milvus FTS filters only support int_field='{self._filter_id_field}'" + raise ValueError(msg) + self.expr = f"{self._filter_id_field} >= {filters.int_value}" + return + msg = f"Not support Filter for Milvus FTS - {filters}" + raise ValueError(msg) if filters.type == FilterOp.NonFilter: self.expr = "" elif filters.type == FilterOp.NumGE: self.expr = f"{self._scalar_id_field} >= {filters.int_value}" elif filters.type == FilterOp.StrEqual: - self.expr = f"{self._scalar_label_field} == '{filters.label_value}'" + self.expr = f"{self._scalar_payload_label_field} == '{filters.label_value}'" else: msg = f"Not support Filter for Milvus - {filters}" raise ValueError(msg) + def supports_payload_profile(self, payload_profile: PayloadProfile) -> bool: + return payload_profile in { + PayloadProfile.IDS_ONLY, + PayloadProfile.VECTOR, + PayloadProfile.SCALAR_LABEL, + } + + def poll_insert_readiness(self, expected_count: int) -> dict: + assert self.client is not None + self.client.flush(self.collection_name) + stats = self.client.get_collection_stats(self.collection_name) + count = int(stats.get("row_count", stats.get("num_entities", 0))) + progress = self.client.describe_index(self.collection_name, self._main_index_name) + return { + "fully_searchable": count >= expected_count, + "fully_indexed": progress.get("pending_index_rows", -1) == 0, + "additional_parameters": {}, + } + def search_embedding( self, query: list[float], k: int = 100, timeout: int | None = None, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + tenant: str | None = None, ) -> list[int]: """Perform a search on a query embedding and return results.""" - assert self.col is not None - - # Perform the search. - res = self.col.search( - data=[query], - anns_field=self._vector_field, - param=self.case_config.search_param(), + assert self.client is not None + + output_fields = None + if payload_profile == PayloadProfile.VECTOR: + output_fields = [self._vector_field] + elif payload_profile == PayloadProfile.SCALAR_LABEL: + output_fields = [getattr(self, "_scalar_payload_label_field", self._scalar_label_field)] + + expr = self.expr + if tenant is not None: + tenant_field = getattr(self, "_multitenant_partition_key_field", self._scalar_label_field) + tenant_expr = f"{tenant_field} == '{tenant}'" + expr = tenant_expr if not expr else f"({expr}) and ({tenant_expr})" + + search_kwargs = { + "collection_name": self.collection_name, + "data": [query], + "anns_field": self._vector_field, + "search_params": self.case_config.search_param(), + "limit": k, + "filter": expr, + "output_fields": output_fields, + } + res = self.client.search(**search_kwargs) + + return [result[self._primary_field] for result in res[0]] + + def search_documents( + self, + query: str, + k: int = 100, + timeout: int | None = None, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + ) -> list[str]: + """Search a Milvus BM25 full-text collection and return document IDs.""" + if not self._is_fts: + msg = "search_documents only valid in FTS mode" + raise RuntimeError(msg) + if not self.supports_document_payload_profile(payload_profile): + msg = f"{getattr(self, 'name', 'Milvus')} does not support document payload_profile={payload_profile.value}" + raise NotImplementedError(msg) + assert self.client is not None + + output_fields = [self._primary_field] + if payload_profile == PayloadProfile.TEXT: + output_fields.append(self._text_field) + + res = self.client.search( + collection_name=self.collection_name, + data=[str(query)], + anns_field=self._sparse_field, + search_params=self.case_config.search_param(), limit=k, - expr=self.expr, + filter=getattr(self, "expr", ""), + output_fields=output_fields, ) - # Organize results. - return [result.id for result in res[0]] + hits = res[0] if res else [] + doc_ids = [] + for hit in hits: + entity = hit.get("entity", hit) if isinstance(hit, dict) else hit + if isinstance(entity, dict): + doc_ids.append(str(entity.get(self._primary_field))) + else: + doc_ids.append(str(getattr(entity, self._primary_field))) + return doc_ids diff --git a/vectordb_bench/backend/clients/oceanbase/cli.py b/vectordb_bench/backend/clients/oceanbase/cli.py index 61583cc82..81ccacc79 100644 --- a/vectordb_bench/backend/clients/oceanbase/cli.py +++ b/vectordb_bench/backend/clients/oceanbase/cli.py @@ -31,9 +31,40 @@ class OceanBaseTypedDict(CommonTypedDict): ] database: Annotated[str, click.option("--database", type=str, help="DataBase name", required=True)] port: Annotated[int, click.option("--port", type=int, help="OceanBase port", required=True)] + create_index_parallel: Annotated[ + int, + click.option( + "--create-index-parallel", + type=int, + default=16, + show_default=True, + help="PARALLEL hint degree for CREATE VECTOR INDEX", + ), + ] + partitions: Annotated[ + int, + click.option( + "--partitions", + type=int, + default=0, + show_default=True, + help="Number of KEY partitions for the table. 0 or 1 means no partitioning.", + ), + ] -class OceanBaseHNSWTypedDict(CommonTypedDict, OceanBaseTypedDict, HNSWFlavor4): ... +class OceanBaseHNSWTypedDict(CommonTypedDict, OceanBaseTypedDict, HNSWFlavor4): + extra_info_max_size: Annotated[ + int | None, + click.option( + "--extra-info-max-size", + type=int, + default=32, + show_default=True, + help="extra_info_max_size for HNSW index. Set to 0 to omit.", + required=False, + ), + ] @cli.command() @@ -55,6 +86,9 @@ def OceanBaseHNSW(**parameters: Unpack[OceanBaseHNSWTypedDict]): m=parameters["m"], efConstruction=parameters["ef_construction"], ef_search=parameters["ef_search"], + extra_info_max_size=parameters["extra_info_max_size"] or None, + create_index_parallel=parameters["create_index_parallel"], + partitions=parameters["partitions"], index=parameters["index_type"], ), **parameters, @@ -94,6 +128,8 @@ def OceanBaseIVF(**parameters: Unpack[OceanBaseIVFTypedDict]): nlist=parameters["nlist"], sample_per_nlist=parameters["sample_per_nlist"], nbits=parameters["nbits"], + create_index_parallel=parameters["create_index_parallel"], + partitions=parameters["partitions"], index=input_index_type, ivf_nprobes=parameters["ivf_nprobes"], ), diff --git a/vectordb_bench/backend/clients/oceanbase/config.py b/vectordb_bench/backend/clients/oceanbase/config.py index 1f37cfc75..384a7c263 100644 --- a/vectordb_bench/backend/clients/oceanbase/config.py +++ b/vectordb_bench/backend/clients/oceanbase/config.py @@ -36,20 +36,18 @@ class OceanBaseIndexConfig(BaseModel): index: IndexType metric_type: MetricType | None = None lib: str = "vsag" + create_index_parallel: int = 16 + partitions: int = 0 def parse_metric(self) -> str: - if self.metric_type == MetricType.L2 or ( - self.index == IndexType.HNSW_BQ and self.metric_type == MetricType.COSINE - ): + if self.metric_type == MetricType.L2: return "l2" if self.metric_type == MetricType.IP: return "inner_product" return "cosine" def parse_metric_func_str(self) -> str: - if self.metric_type == MetricType.L2 or ( - self.index == IndexType.HNSW_BQ and self.metric_type == MetricType.COSINE - ): + if self.metric_type == MetricType.L2: return "l2_distance" if self.metric_type == MetricType.IP: return "negative_inner_product" @@ -60,6 +58,7 @@ class OceanBaseHNSWConfig(OceanBaseIndexConfig, DBCaseConfig): m: int efConstruction: int ef_search: int | None = None + extra_info_max_size: int | None = 32 index: IndexType def index_param(self) -> dict: diff --git a/vectordb_bench/backend/clients/oceanbase/oceanbase.py b/vectordb_bench/backend/clients/oceanbase/oceanbase.py index 93c42aac1..a34161037 100644 --- a/vectordb_bench/backend/clients/oceanbase/oceanbase.py +++ b/vectordb_bench/backend/clients/oceanbase/oceanbase.py @@ -23,6 +23,8 @@ class OceanBase(VectorDB): FilterOp.NumGE, FilterOp.StrEqual, ] + # mysql-connector cursor cannot be shared across threads + thread_safe: bool = False def __init__( self, @@ -109,27 +111,28 @@ def _create_table(self): if not self._cursor: raise ValueError("Cursor is not initialized") - log.info(f"Creating table {self.table_name}") - create_table_query = f""" - CREATE TABLE {self.table_name} ( - id INT PRIMARY KEY, - embedding VECTOR({self.dim}) - ); - """ + partitions = getattr(self.db_case_config, "partitions", 0) + log.info(f"Creating table {self.table_name} (partitions={partitions})") + + create_table_query = f"CREATE TABLE {self.table_name} (id INT PRIMARY KEY, embedding VECTOR({self.dim}))" + if partitions > 1: + create_table_query += f" PARTITION BY KEY(id) PARTITIONS {partitions}" + create_table_query += ";" self._cursor.execute(create_table_query) def optimize(self, data_size: int): index_params = self.db_case_config.index_param() index_args = ", ".join(f"{k}={v}" for k, v in index_params["params"].items()) index_query = ( - f"CREATE /*+ PARALLEL(18) */ VECTOR INDEX idx1 " + f"CREATE /*+ PARALLEL({self.db_case_config.create_index_parallel}) */ VECTOR INDEX idx1 " f"ON {self.table_name}(embedding) " f"WITH (distance={self.db_case_config.parse_metric()}, " f"type={index_params['index_type']}, lib={index_params['lib']}, {index_args}" ) - if self.db_case_config.index in {IndexType.HNSW, IndexType.HNSW_SQ, IndexType.HNSW_BQ}: - index_query += ", extra_info_max_size=32" + extra_info = getattr(self.db_case_config, "extra_info_max_size", None) + if extra_info is not None: + index_query += f", extra_info_max_size={extra_info}" index_query += ")" @@ -153,10 +156,6 @@ def optimize(self, data_size: int): raise def need_normalize_cosine(self) -> bool: - if self.db_case_config.index == IndexType.HNSW_BQ: - log.info("current HNSW_BQ only supports L2, cosine dataset need normalize.") - return True - return False def _wait_for_major_compaction(self): @@ -185,9 +184,7 @@ def insert_embeddings( batch_end = min(batch_start + self.load_batch_size, len(embeddings)) batch = [(metadata[i], embeddings[i]) for i in range(batch_start, batch_end)] values = ", ".join(f"({item_id}, '[{','.join(map(str, embedding))}]')" for item_id, embedding in batch) - self._cursor.execute( - f"INSERT /*+ ENABLE_PARALLEL_DML PARALLEL(32) */ INTO {self.table_name} VALUES {values}" # noqa: S608 - ) + self._cursor.execute(f"INSERT INTO {self.table_name} VALUES {values}") insert_count += len(batch) except mysql.Error: log.exception("Failed to insert embeddings") @@ -217,7 +214,7 @@ def search_embedding( packed = struct.pack(f"<{len(query)}f", *query) hex_vec = packed.hex() query_str = ( - f"SELECT id FROM {self.table_name} " # noqa: S608 + f"SELECT id FROM {self.table_name} " f"{self.expr} ORDER BY " f"{self.db_case_config.parse_metric_func_str()}(embedding, X'{hex_vec}') " f"APPROXIMATE LIMIT {k}" diff --git a/vectordb_bench/backend/clients/oss_opensearch/cli.py b/vectordb_bench/backend/clients/oss_opensearch/cli.py index 0c4b694ea..03da18fdb 100644 --- a/vectordb_bench/backend/clients/oss_opensearch/cli.py +++ b/vectordb_bench/backend/clients/oss_opensearch/cli.py @@ -11,6 +11,7 @@ click_parameter_decorators_from_typed_dict, run, ) +from ...cases import CaseType from .. import DB from .config import OSSOpenSearchQuantization, OSSOS_Engine @@ -18,6 +19,7 @@ class OSSOpenSearchTypedDict(TypedDict): + index_name: Annotated[str, click.option("--index-name", type=str, help="Db index name", default="vdb_bench_index")] host: Annotated[str, click.option("--host", type=str, help="Db host", required=True)] port: Annotated[int, click.option("--port", type=int, default=80, help="Db Port")] user: Annotated[str, click.option("--user", type=str, help="Db User")] @@ -29,7 +31,7 @@ class OSSOpenSearchTypedDict(TypedDict): number_of_replicas: Annotated[ int, click.option( - "--number-of-replicas", type=int, help="Number of replica copies for each primary shard", default=1 + "--number-of-replicas", type=int, help="Number of replica copies for each primary shard", default=0 ), ] index_thread_qty: Annotated[ @@ -147,17 +149,17 @@ class OSSOpenSearchHNSWTypedDict(CommonTypedDict, OSSOpenSearchTypedDict, HNSWFl @cli.command() @click_parameter_decorators_from_typed_dict(OSSOpenSearchHNSWTypedDict) def OSSOpenSearch(**parameters: Unpack[OSSOpenSearchHNSWTypedDict]): - from .config import OSSOpenSearchConfig, OSSOpenSearchIndexConfig + from .config import OSSOpenSearchConfig, OSSOpenSearchFtsConfig, OSSOpenSearchIndexConfig - run( - db=DB.OSSOpenSearch, - db_config=OSSOpenSearchConfig( - host=parameters["host"], - port=parameters["port"], - user=parameters["user"], - password=SecretStr(parameters["password"]), - ), - db_case_config=OSSOpenSearchIndexConfig( + if parameters["case_type"] == CaseType.FTSBm25Performance.name: + db_case_config = OSSOpenSearchFtsConfig( + number_of_shards=parameters["number_of_shards"], + number_of_replicas=parameters["number_of_replicas"], + refresh_interval=parameters["refresh_interval"], + force_merge_enabled=parameters["force_merge_enabled"], + ) + else: + db_case_config = OSSOpenSearchIndexConfig( number_of_shards=parameters["number_of_shards"], number_of_replicas=parameters["number_of_replicas"], index_thread_qty=parameters["index_thread_qty"], @@ -174,6 +176,17 @@ def OSSOpenSearch(**parameters: Unpack[OSSOpenSearchHNSWTypedDict]): quantization_type=OSSOpenSearchQuantization(parameters["quantization_type"]), confidence_interval=parameters["confidence_interval"], clip=parameters["clip"], + ) + + run( + db=DB.OSSOpenSearch, + db_config=OSSOpenSearchConfig( + index_name=parameters["index_name"], + host=parameters["host"], + port=parameters["port"], + user=parameters["user"], + password=SecretStr(parameters["password"]), ), + db_case_config=db_case_config, **parameters, ) diff --git a/vectordb_bench/backend/clients/oss_opensearch/config.py b/vectordb_bench/backend/clients/oss_opensearch/config.py index d153eb429..05f8f584a 100644 --- a/vectordb_bench/backend/clients/oss_opensearch/config.py +++ b/vectordb_bench/backend/clients/oss_opensearch/config.py @@ -1,15 +1,19 @@ import logging from enum import Enum -from typing import Any +from typing import ClassVar from pydantic import BaseModel, SecretStr, field_validator, model_validator from ..api import DBCaseConfig, DBConfig, MetricType +from ..elasticsearch_compatible import build_bm25_similarity_settings, build_fts_index_param log = logging.getLogger(__name__) class OSSOpenSearchConfig(DBConfig, BaseModel): + _extra_empty_skip: ClassVar[frozenset[str]] = frozenset({"user", "password", "host"}) + + index_name: str = "vdb_bench_index" host: str = "" port: int = 80 user: str | None = None @@ -23,6 +27,7 @@ def to_dict(self) -> dict: else () ) return { + "index_name": self.index_name, "hosts": [{"host": self.host, "port": self.port}], "http_auth": http_auth, "use_ssl": use_ssl, @@ -33,19 +38,6 @@ def to_dict(self) -> dict: "timeout": 600, } - @model_validator(mode="before") - @classmethod - def not_empty_field(cls, data: Any) -> Any: - if isinstance(data, dict): - skip = set(cls.common_short_configs()) | set(cls.common_long_configs()) | {"user", "password", "host"} - for name, v in data.items(): - if name in skip: - continue - if isinstance(v, str) and len(v) == 0: - msg = f"Empty string for field '{name}'!" - raise ValueError(msg) - return data - class OSSOS_Engine(Enum): faiss = "faiss" @@ -115,7 +107,7 @@ class OSSOpenSearchIndexConfig(BaseModel, DBCaseConfig): @field_validator("quantization_type", mode="before") @classmethod - def validate_quantization_type(cls, value: Any) -> OSSOpenSearchQuantization: + def validate_quantization_type(cls, value: any): """Convert string values to enum""" if not value: return OSSOpenSearchQuantization.NONE @@ -133,9 +125,11 @@ def validate_quantization_type(cls, value: Any) -> OSSOpenSearchQuantization: @model_validator(mode="before") @classmethod - def validate_engine_name(cls, data: Any) -> Any: - """Map engine_name string from UI to engine enum""" - if isinstance(data, dict) and data.get("engine_name"): + def validate_engine_name(cls, data: any) -> any: + if not isinstance(data, dict): + return data + # Map engine_name to engine enum + if data.get("engine_name"): engine_name = data["engine_name"].lower() if engine_name == "faiss": data["engine"] = OSSOS_Engine.faiss @@ -256,3 +250,22 @@ def index_param(self) -> dict: def search_param(self) -> dict: return {"ef_search": self.efSearch} + + +class OSSOpenSearchFtsConfig(BaseModel, DBCaseConfig): + number_of_shards: int = 1 + number_of_replicas: int = 0 + refresh_interval: str = "30s" + force_merge_enabled: bool = True + metric_type: MetricType = MetricType.BM25 + bm25_k1: float | None = None + bm25_b: float | None = None + + def index_param(self) -> dict: + return build_fts_index_param(self.bm25_k1, self.bm25_b) + + def search_param(self) -> dict: + return {} + + def similarity_settings(self) -> dict: + return build_bm25_similarity_settings(self.bm25_k1, self.bm25_b) diff --git a/vectordb_bench/backend/clients/oss_opensearch/oss_opensearch.py b/vectordb_bench/backend/clients/oss_opensearch/oss_opensearch.py index f71850a17..d26514497 100644 --- a/vectordb_bench/backend/clients/oss_opensearch/oss_opensearch.py +++ b/vectordb_bench/backend/clients/oss_opensearch/oss_opensearch.py @@ -9,15 +9,16 @@ from packaging.version import parse as parse_version from vectordb_bench.backend.filter import Filter, FilterOp +from vectordb_bench.backend.payload import PayloadProfile from ..api import VectorDB -from .config import OSSOpenSearchIndexConfig, OSSOS_Engine +from .config import OSSOpenSearchFtsConfig, OSSOpenSearchIndexConfig, OSSOS_Engine log = logging.getLogger(__name__) WAITING_FOR_REFRESH_SEC: Final[int] = 30 WAITING_FOR_FORCE_MERGE_SEC: Final[int] = 30 -SECONDS_WAITING_FOR_REPLICAS_TO_BE_ENABLED_SEC: Final[int] = 30 +REPLICA_HEALTH_TIMEOUT: Final[str] = "30m" # Central registry for version-dependent OpenSearch index settings. # Add new rules here to automatically support future versions. @@ -194,7 +195,7 @@ def __init__( self, dim: int, db_config: dict[str, Any], - db_case_config: OSSOpenSearchIndexConfig, + db_case_config: OSSOpenSearchIndexConfig | OSSOpenSearchFtsConfig, index_name: str = "vdb_bench_index", # must be lowercase id_col_name: str = "_id", label_col_name: str = "label", @@ -205,13 +206,19 @@ def __init__( ) -> None: """Initialize the OpenSearch client.""" self.dim = dim - self.db_config = db_config + self.db_config = dict(db_config) + configured_index_name = self.db_config.pop("index_name", None) self.case_config = db_case_config - self.index_name = index_name + self.index_name = configured_index_name or index_name self.id_col_name = id_col_name self.label_col_name = label_col_name self.vector_col_name = vector_col_name self.with_scalar_labels = with_scalar_labels + self._is_fts = isinstance(db_case_config, OSSOpenSearchFtsConfig) + self.text_col_name = "text" + self.filter_id_col_name = "filter_id" + if self._is_fts: + self.id_col_name = "doc_id" # Initialize client state self.client: OpenSearch | None = None @@ -236,13 +243,21 @@ def _handle_index_initialization(self, client: OpenSearch, drop_old: bool) -> No if not is_existed: self._create_index(client) log.info(f"OSS_OpenSearch client create index: {self.index_name}") - self._update_ef_search_before_search(client) - self._load_graphs_to_memory(client) + if not self._is_fts: + self._update_ef_search_before_search(client) + self._load_graphs_to_memory(client) def need_normalize_cosine(self) -> bool: """Whether this database needs to normalize dataset to support COSINE metric.""" return True + @classmethod + def supports_full_text_search(cls) -> bool: + return True + + def has_text_field(self) -> bool: + return bool(getattr(self, "_is_fts", False) and getattr(self, "text_col_name", None)) + def _get_cluster_version(self, client: OpenSearch) -> Version: """ Return the OpenSearch cluster version as a comparable Version object. @@ -307,7 +322,31 @@ def _get_bulk_manager(self, client: OpenSearch) -> BulkInsertManager: """Get bulk insert manager for the given client.""" return BulkInsertManager(client, self.index_name, self.case_config) + def _create_fts_index(self, client: OpenSearch) -> None: + mappings = self.case_config.index_param() + index_settings = { + "number_of_shards": self.case_config.number_of_shards, + "number_of_replicas": self.case_config.number_of_replicas, + "refresh_interval": self.case_config.refresh_interval, + } + index_settings.update(self.case_config.similarity_settings()) + settings = {"index": index_settings} + try: + log.info(f"Creating FTS index with settings: {settings}") + log.info(f"Creating FTS index with mappings: {mappings}") + client.indices.create( + index=self.index_name, + body={"settings": settings, "mappings": mappings}, + ) + except Exception as e: + log.warning(f"Failed to create FTS index: {self.index_name} error: {e!s}") + raise e from None + def _create_index(self, client: OpenSearch) -> None: + if self._is_fts: + self._create_fts_index(client) + return + cluster_version = self._get_cluster_version(client) if self.case_config.on_disk and cluster_version < Version("2.17"): @@ -420,6 +459,94 @@ def insert_embeddings( log.info(f"Using {num_clients} parallel clients for data insertion") return self._insert_with_multiple_clients(embeddings, metadata, num_clients, labels_data) + def insert_documents( + self, + texts: Iterable[str], + doc_ids: list[str], + **kwargs: Any, + ) -> tuple[int, Exception | None]: + if not getattr(self, "_is_fts", False): + msg = "OSSOpenSearch full-text insert requires OSSOpenSearchFtsConfig" + raise RuntimeError(msg) + assert self.client is not None, "should self.init() first" + docs = list(texts) + if len(docs) != len(doc_ids): + msg = f"Mismatch between texts ({len(docs)}) and doc_ids ({len(doc_ids)}) lengths" + raise ValueError(msg) + filter_ids = kwargs.get("filter_ids") + if filter_ids is not None and len(filter_ids) != len(docs): + msg = f"Mismatch between texts ({len(docs)}) and filter_ids ({len(filter_ids)}) lengths" + raise ValueError(msg) + + insert_data: list[dict[str, Any]] = [] + for i, doc in enumerate(docs): + doc_id = str(doc_ids[i]) + source = {self.id_col_name: doc_id, self.text_col_name: doc} + if filter_ids is not None: + source[self.filter_id_col_name] = int(filter_ids[i]) + insert_data.append({"index": {"_index": self.index_name, "_id": doc_id}}) + insert_data.append(source) + + try: + response = self.client.bulk(body=insert_data) + except Exception as e: + log.warning(f"Failed to insert FTS docs: {self.index_name} error: {e!s}") + return 0, e + insert_count, error = self._parse_fts_bulk_response(response, len(docs)) + if error is not None: + log.warning("FTS bulk insert failed: %s", error) + return insert_count, error + + @staticmethod + def _parse_fts_bulk_response(response: dict[str, Any], expected_count: int) -> tuple[int, Exception | None]: + if not response.get("errors"): + return expected_count, None + + items = response.get("items") + if not isinstance(items, list): + error = RuntimeError("OpenSearch FTS bulk response reported errors without an items list") + return 0, error + + success_count = 0 + failure_samples = [] + for position, item in enumerate(items): + if not isinstance(item, dict) or len(item) != 1: + failure_samples.append(f"item[{position}]=malformed") + continue + + operation, result = next(iter(item.items())) + if not isinstance(result, dict): + failure_samples.append(f"item[{position}] {operation}=malformed") + continue + + status = result.get("status") + if isinstance(status, int) and 200 <= status < 300 and "error" not in result: + success_count += 1 + continue + + error_detail = result.get("error") + if isinstance(error_detail, dict): + error_type = error_detail.get("type", "unknown") + reason = error_detail.get("reason", "unknown") + error_summary = f"{error_type}: {reason}" + else: + error_summary = str(error_detail or "unknown") + failure_samples.append( + f"item[{position}] {operation} id={result.get('_id', 'unknown')} " + f"status={status or 'unknown'} error={error_summary}" + ) + + success_count = min(success_count, expected_count) + failed_count = max(expected_count - success_count, 1) + sample_summary = "; ".join(failure_samples[:3]) or "no failed item details" + if len(items) != expected_count: + sample_summary = f"response_items={len(items)}, expected_items={expected_count}; {sample_summary}" + error = RuntimeError( + f"OpenSearch FTS bulk insert failed for {failed_count}/{expected_count} documents; " + f"successful={success_count}; {sample_summary}" + ) + return success_count, error + def _insert_with_single_client( self, embeddings: Iterable[list[float]], @@ -569,14 +696,67 @@ def search_embedding( log.warning(f"Failed to search: {self.index_name} error: {e!s}") raise e from None + def search_documents( + self, + query: str, + k: int = 100, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + **kwargs: Any, + ) -> list[str]: + if not getattr(self, "_is_fts", False): + msg = "OSSOpenSearch full-text search requires OSSOpenSearchFtsConfig" + raise RuntimeError(msg) + if not self.supports_document_payload_profile(payload_profile): + msg = f"OSSOpenSearch does not support document payload_profile={payload_profile.value}" + raise NotImplementedError(msg) + assert self.client is not None, "should self.init() first" + + source = [self.text_col_name] if payload_profile == PayloadProfile.TEXT else False + filter_path = ["hits.hits._id", f"hits.hits.fields.{self.id_col_name}"] + if payload_profile == PayloadProfile.TEXT: + filter_path.append(f"hits.hits._source.{self.text_col_name}") + query_clause: dict[str, Any] = {"match": {self.text_col_name: query}} + if self.filter: + query_clause = {"bool": {"must": query_clause, "filter": self.filter}} + search_kwargs: dict[str, Any] = { + "index": self.index_name, + "body": {"query": query_clause}, + "size": k, + "_source": source, + "docvalue_fields": [self.id_col_name], + "filter_path": filter_path, + } + if payload_profile != PayloadProfile.TEXT: + search_kwargs["stored_fields"] = "_none_" + response = self.client.search(**search_kwargs) + + doc_ids = [] + for hit in response.get("hits", {}).get("hits", []): + if hit.get("_id") is not None: + doc_ids.append(str(hit["_id"])) + continue + values = hit.get("fields", {}).get(self.id_col_name, []) + if values: + doc_ids.append(str(values[0])) + return doc_ids + def prepare_filter(self, filters: Filter) -> None: """Prepare filter conditions for search operations.""" self.routing_key = None if filters.type == FilterOp.NonFilter: self.filter = None elif filters.type == FilterOp.NumGE: - self.filter = {"range": {self.id_col_name: {"gt": filters.int_value}}} + if self._is_fts: + if getattr(filters, "int_field", None) != self.filter_id_col_name: + msg = f"OSSOpenSearch FTS filters only support int_field='{self.filter_id_col_name}'" + raise ValueError(msg) + self.filter = {"range": {self.filter_id_col_name: {"gte": filters.int_value}}} + else: + self.filter = {"range": {self.id_col_name: {"gt": filters.int_value}}} elif filters.type == FilterOp.StrEqual: + if self._is_fts: + msg = f"Not support Filter for OSSOpenSearch FTS - {filters}" + raise ValueError(msg) self.filter = {"term": {self.label_col_name: filters.label_value}} if self.case_config.use_routing: self.routing_key = filters.label_value @@ -587,6 +767,14 @@ def prepare_filter(self, filters: Filter) -> None: def optimize(self, data_size: int | None = None) -> None: """Optimize the index for better search performance.""" + if self._is_fts: + self._refresh_index() + if self.case_config.force_merge_enabled: + self._do_fts_force_merge() + self._refresh_index() + self._update_replicas() + self._refresh_index() + return self._update_ef_search() # Call refresh first to ensure that all segments are created self._refresh_index() @@ -621,13 +809,20 @@ def _update_replicas(self): def _wait_till_green(self): log.info("Wait for index to become green..") - while True: - res = self.client.cat.indices(index=self.index_name, h="health", format="json") - health = res[0]["health"] - if health == "green": - break - log.info(f"The index {self.index_name} has health : {health} and is not green. Retrying") - time.sleep(SECONDS_WAITING_FOR_REPLICAS_TO_BE_ENABLED_SEC) + response = self.client.cluster.health( + index=self.index_name, + wait_for_status="green", + timeout=REPLICA_HEALTH_TIMEOUT, + ) + health = response.get("status", "unknown") + if response.get("timed_out") or health != "green": + msg = ( + f"Index {self.index_name} did not reach green health within {REPLICA_HEALTH_TIMEOUT}: " + f"status={health}, configured_replicas={self.case_config.number_of_replicas}, " + f"unassigned_shards={response.get('unassigned_shards', 'unknown')}, " + f"number_of_nodes={response.get('number_of_nodes', 'unknown')}" + ) + raise OpenSearchError(msg) log.info(f"Index {self.index_name} is green..") def _refresh_index(self): @@ -645,6 +840,17 @@ def _refresh_index(self): continue log.debug(f"Completed refresh for index {self.index_name}") + def _do_fts_force_merge(self): + log.info(f"Starting FTS force merge for index {self.index_name}") + force_merge_endpoint = f"/{self.index_name}/_forcemerge?max_num_segments=1&wait_for_completion=false" + force_merge_task_id = self.client.transport.perform_request("POST", force_merge_endpoint)["task"] + while True: + time.sleep(WAITING_FOR_FORCE_MERGE_SEC) + task_status = self.client.tasks.get(task_id=force_merge_task_id) + if task_status["completed"]: + break + log.info(f"Completed FTS force merge for index {self.index_name}") + def _do_force_merge(self): log.info(f"Updating the Index thread qty to {self.case_config.index_thread_qty_during_force_merge}.") diff --git a/vectordb_bench/backend/clients/pgdiskann/cli.py b/vectordb_bench/backend/clients/pgdiskann/cli.py index f1e44e2f2..4f372c779 100644 --- a/vectordb_bench/backend/clients/pgdiskann/cli.py +++ b/vectordb_bench/backend/clients/pgdiskann/cli.py @@ -80,7 +80,7 @@ class PgDiskAnnTypedDict(CommonTypedDict): click.option( "--reranking-metric", type=click.Choice( - [metric.value for metric in MetricType if metric.value not in ["HAMMING", "JACCARD", "DP"]], + [metric.value for metric in MetricType if metric.value not in ["HAMMING", "JACCARD", "DP", "BM25"]], ), help="Distance metric for reranking", default="COSINE", diff --git a/vectordb_bench/backend/clients/pgdiskann/pgdiskann.py b/vectordb_bench/backend/clients/pgdiskann/pgdiskann.py index 5f069ace5..46e8fabd4 100644 --- a/vectordb_bench/backend/clients/pgdiskann/pgdiskann.py +++ b/vectordb_bench/backend/clients/pgdiskann/pgdiskann.py @@ -10,6 +10,8 @@ from pgvector.psycopg import register_vector from psycopg import Connection, Cursor, sql +from vectordb_bench.backend.filter import Filter, FilterOp + from ..api import VectorDB from .config import PgDiskANNConfigDict, PgDiskANNIndexConfig @@ -19,11 +21,16 @@ class PgDiskANN(VectorDB): """Use psycopg instructions""" + supported_filter_types: list[FilterOp] = [ + FilterOp.NonFilter, + FilterOp.NumGE, + FilterOp.StrEqual, + ] + conn: psycopg.Connection[Any] | None = None - coursor: psycopg.Cursor[Any] | None = None + cursor: psycopg.Cursor[Any] | None = None - _filtered_search: sql.Composed - _unfiltered_search: sql.Composed + _search: sql.Composed def __init__( self, @@ -32,6 +39,7 @@ def __init__( db_case_config: PgDiskANNIndexConfig, collection_name: str = "pg_diskann_collection", drop_old: bool = False, + with_scalar_labels: bool = False, **kwargs, ): self.name = "PgDiskANN" @@ -39,6 +47,9 @@ def __init__( self.case_config = db_case_config self.table_name = collection_name self.dim = dim + self.with_scalar_labels = with_scalar_labels + self._scalar_label_field = "label" + self.where_clause = "" self._index_name = "pgdiskann_index" self._primary_field = "id" @@ -86,83 +97,58 @@ def _create_connection(**kwargs) -> tuple[Connection, Cursor]: return conn, cursor - @contextmanager - def init(self) -> Generator[None, None, None]: - self.conn, self.cursor = self._create_connection(**self.db_config) - - session_options: dict[str, Any] = self.case_config.session_param() - - if len(session_options) > 0: - for setting_name, setting_val in session_options.items(): - command = sql.SQL("SET {setting_name} = {setting_val};").format( - setting_name=sql.Identifier(setting_name), setting_val=sql.Literal(setting_val) - ) - log.debug(command.as_string(self.cursor)) - self.cursor.execute(command) - self.conn.commit() - + def _generate_search_query(self) -> sql.Composed: + """Generate search query with where_clause placeholder""" search_params = self.case_config.search_param() if search_params.get("reranking"): - # Reranking-enabled queries - self._filtered_search = sql.SQL(""" + search_query = sql.SQL(""" SELECT i.id FROM ( SELECT id, embedding FROM public.{table_name} - WHERE id >= %s + {where_clause} ORDER BY embedding {metric_fun_op} %s::vector LIMIT {quantized_fetch_limit}::int ) i ORDER BY i.embedding {reranking_metric_fun_op} %s::vector LIMIT %s::int - """).format( + """).format( table_name=sql.Identifier(self.table_name), + where_clause=sql.SQL(self.where_clause), metric_fun_op=sql.SQL(search_params["metric_fun_op"]), reranking_metric_fun_op=sql.SQL(search_params["reranking_metric_fun_op"]), quantized_fetch_limit=sql.Literal(search_params["quantized_fetch_limit"]), ) - - self._unfiltered_search = sql.SQL(""" - SELECT i.id - FROM ( - SELECT id, embedding - FROM public.{table_name} - ORDER BY embedding {metric_fun_op} %s::vector - LIMIT {quantized_fetch_limit}::int - ) i - ORDER BY i.embedding {reranking_metric_fun_op} %s::vector - LIMIT %s::int - """).format( - table_name=sql.Identifier(self.table_name), - metric_fun_op=sql.SQL(search_params["metric_fun_op"]), - reranking_metric_fun_op=sql.SQL(search_params["reranking_metric_fun_op"]), - quantized_fetch_limit=sql.Literal(search_params["quantized_fetch_limit"]), - ) - else: - self._filtered_search = sql.Composed( + search_query = sql.Composed( [ - sql.SQL( - "SELECT id FROM public.{table_name} WHERE id >= %s ORDER BY embedding ", - ).format(table_name=sql.Identifier(self.table_name)), - sql.SQL(search_params["metric_fun_op"]), - sql.SQL(" %s::vector LIMIT %s::int"), - ] - ) - - self._unfiltered_search = sql.Composed( - [ - sql.SQL("SELECT id FROM public.{table_name} ORDER BY embedding ").format( - table_name=sql.Identifier(self.table_name) + sql.SQL("SELECT id FROM public.{table_name} {where_clause} ORDER BY embedding ").format( + table_name=sql.Identifier(self.table_name), + where_clause=sql.SQL(self.where_clause), ), sql.SQL(search_params["metric_fun_op"]), sql.SQL(" %s::vector LIMIT %s::int"), ] ) - log.debug(f"Unfiltered search query={self._unfiltered_search.as_string(self.conn)}") - log.debug(f"Filtered search query={self._filtered_search.as_string(self.conn)}") + return search_query + + @contextmanager + def init(self) -> Generator[None, None, None]: + self.conn, self.cursor = self._create_connection(**self.db_config) + + session_options: dict[str, Any] = self.case_config.session_param() + + if len(session_options) > 0: + for setting_name, setting_val in session_options.items(): + command = sql.SQL("SET {setting_name} = {setting_val};").format( + setting_name=sql.Identifier(setting_name), + setting_val=sql.Literal(setting_val), + ) + log.debug(command.as_string(self.cursor)) + self.cursor.execute(command) + self.conn.commit() try: yield @@ -281,12 +267,10 @@ def _create_index(self): with_clause = sql.SQL("WITH ({});").format(sql.SQL(", ").join(options)) if any(options) else sql.Composed(()) - index_create_sql = sql.SQL( - """ + index_create_sql = sql.SQL(""" CREATE INDEX IF NOT EXISTS {index_name} ON public.{table_name} USING {index_type} (embedding {embedding_metric}) - """, - ).format( + """).format( index_name=sql.Identifier(self._index_name), table_name=sql.Identifier(self.table_name), index_type=sql.Identifier(index_param["index_type"].lower()), @@ -304,11 +288,36 @@ def _create_table(self, dim: int): try: log.info(f"{self.name} client create table : {self.table_name}") + if self.with_scalar_labels: + self.cursor.execute( + sql.SQL(""" + CREATE TABLE IF NOT EXISTS public.{table_name} + ({primary_field} BIGINT PRIMARY KEY, embedding vector({dim}), {label_field} VARCHAR(64)); + """).format( + table_name=sql.Identifier(self.table_name), + dim=dim, + primary_field=sql.Identifier(self._primary_field), + label_field=sql.Identifier(self._scalar_label_field), + ), + ) + else: + self.cursor.execute( + sql.SQL(""" + CREATE TABLE IF NOT EXISTS public.{table_name} + ({primary_field} BIGINT PRIMARY KEY, embedding vector({dim})); + """).format( + table_name=sql.Identifier(self.table_name), + dim=dim, + primary_field=sql.Identifier(self._primary_field), + ), + ) + self.cursor.execute( - sql.SQL( - "CREATE TABLE IF NOT EXISTS public.{table_name} (id BIGINT PRIMARY KEY, embedding vector({dim}));", - ).format(table_name=sql.Identifier(self.table_name), dim=dim), + sql.SQL("ALTER TABLE public.{table_name} ALTER COLUMN embedding SET STORAGE PLAIN;").format( + table_name=sql.Identifier(self.table_name) + ), ) + self.conn.commit() except Exception as e: log.warning(f"Failed to create pgdiskann table: {self.table_name} error: {e}") @@ -318,11 +327,15 @@ def insert_embeddings( self, embeddings: list[list[float]], metadata: list[int], + labels_data: list[str] | None = None, **kwargs: Any, ) -> tuple[int, Exception | None]: assert self.conn is not None, "Connection is not initialized" assert self.cursor is not None, "Cursor is not initialized" + if self.with_scalar_labels: + assert labels_data is not None, "labels_data should be provided if with_scalar_labels is set to True" + try: metadata_arr = np.array(metadata) embeddings_arr = np.array(embeddings) @@ -332,9 +345,14 @@ def insert_embeddings( table_name=sql.Identifier(self.table_name), ), ) as copy: - copy.set_types(["bigint", "vector"]) - for i, row in enumerate(metadata_arr): - copy.write_row((row, embeddings_arr[i])) + if self.with_scalar_labels: + copy.set_types(["bigint", "vector", "varchar"]) + for i, row in enumerate(metadata_arr): + copy.write_row((row, embeddings_arr[i], labels_data[i])) + else: + copy.set_types(["bigint", "vector"]) + for i, row in enumerate(metadata_arr): + copy.write_row((row, embeddings_arr[i])) self.conn.commit() if kwargs.get("last_batch"): @@ -345,49 +363,39 @@ def insert_embeddings( log.warning(f"Failed to insert data into table ({self.table_name}), error: {e}") return 0, e + def prepare_filter(self, filters: Filter): + """Prepare filter - builds where_clause""" + if filters.type == FilterOp.NonFilter: + self.where_clause = "" + elif filters.type == FilterOp.NumGE: + self.where_clause = f"WHERE {self._primary_field} >= {filters.int_value}" + elif filters.type == FilterOp.StrEqual: + self.where_clause = f"WHERE {self._scalar_label_field} = '{filters.label_value}'" + else: + msg = f"Not support Filter for PgDiskANN - {filters}" + raise ValueError(msg) + + self._search = self._generate_search_query() + log.debug(f"Search query={self._search.as_string(self.conn)}") + def search_embedding( self, query: list[float], k: int = 100, - filters: dict | None = None, timeout: int | None = None, + **kwargs: Any, ) -> list[int]: assert self.conn is not None, "Connection is not initialized" assert self.cursor is not None, "Cursor is not initialized" search_params = self.case_config.search_param() - is_reranking = search_params.get("reranking", False) - q = np.asarray(query) - if filters: - gt = filters.get("id") - if is_reranking: - result = self.cursor.execute( - self._filtered_search, - (gt, q, q, k), - prepare=True, - binary=True, - ) - else: - result = self.cursor.execute( - self._filtered_search, - (gt, q, k), - prepare=True, - binary=True, - ) - elif is_reranking: - result = self.cursor.execute( - self._unfiltered_search, - (q, q, k), - prepare=True, - binary=True, - ) - else: - result = self.cursor.execute( - self._unfiltered_search, - (q, k), - prepare=True, - binary=True, - ) + + result = self.cursor.execute( + self._search, + (q, q, k) if search_params.get("reranking", False) else (q, k), + prepare=True, + binary=True, + ) return [int(i[0]) for i in result.fetchall()] diff --git a/vectordb_bench/backend/clients/pgvector/cli.py b/vectordb_bench/backend/clients/pgvector/cli.py index 2d56c4815..7da41d5c6 100644 --- a/vectordb_bench/backend/clients/pgvector/cli.py +++ b/vectordb_bench/backend/clients/pgvector/cli.py @@ -109,7 +109,7 @@ class PgVectorTypedDict(CommonTypedDict): click.option( "--reranking-metric", type=click.Choice( - [metric.value for metric in MetricType if metric.value not in ["HAMMING", "JACCARD"]], + [metric.value for metric in MetricType if metric.value not in ["HAMMING", "JACCARD", "BM25"]], ), help="Distance metric for reranking", default="COSINE", diff --git a/vectordb_bench/backend/clients/pgvector/pgvector.py b/vectordb_bench/backend/clients/pgvector/pgvector.py index 42fa7533d..41060af27 100644 --- a/vectordb_bench/backend/clients/pgvector/pgvector.py +++ b/vectordb_bench/backend/clients/pgvector/pgvector.py @@ -21,6 +21,7 @@ class PgVector(VectorDB): """Use psycopg instructions""" + thread_safe: bool = False supported_filter_types: list[FilterOp] = [ FilterOp.NonFilter, FilterOp.NumGE, @@ -334,6 +335,13 @@ def _create_index(self): index_param = self.case_config.index_param() self._set_parallel_index_build_param() + # pgvector registers access methods in lowercase ("hnsw", "ivfflat") but + # IndexType enum values are uppercase; also IVFFlat maps to "ivfflat" (no underscore). + index_type_lower = index_param["index_type"].lower() + if index_type_lower == "ivf_flat": + index_type_lower = "ivfflat" + log.info(f"index_type (original={index_param['index_type']}, normalized={index_type_lower})") + options = [] for option in index_param["index_creation_with_options"]: if option["val"] is not None: @@ -359,8 +367,8 @@ def _create_index(self): if index_param["quantization_type"] == "bit" else sql.Identifier("embedding") ), - index_type=sql.Identifier(index_param["index_type"]), - # This assumes that the quantization_type value matches the quantization function name + index_type=sql.Identifier(index_type_lower), + # quantization_type value matches the quantization function name quantization_type=sql.SQL(index_param["quantization_type"]), dim=self.dim, embedding_metric=sql.Identifier(index_param["metric"]), @@ -374,7 +382,7 @@ def _create_index(self): ).format( index_name=sql.Identifier(self._index_name), table_name=sql.Identifier(self.table_name), - index_type=sql.Identifier(index_param["index_type"]), + index_type=sql.Identifier(index_type_lower), embedding_metric=sql.Identifier(index_param["metric"]), ) diff --git a/vectordb_bench/backend/clients/pinecone/cli.py b/vectordb_bench/backend/clients/pinecone/cli.py index 03e1b5f1a..0529fc691 100644 --- a/vectordb_bench/backend/clients/pinecone/cli.py +++ b/vectordb_bench/backend/clients/pinecone/cli.py @@ -26,10 +26,6 @@ class PineconeTypedDict(TypedDict): str, click.option("--version", type=str, help="Database version", default="", show_default=True), ] - note: Annotated[ - str, - click.option("--note", type=str, help="Additional notes", default="", show_default=True), - ] class PineconeIndexTypedDict(CommonTypedDict, PineconeTypedDict): ... @@ -45,7 +41,6 @@ def Pinecone(**parameters: Unpack[PineconeIndexTypedDict]): db_config=PineconeConfig( db_label=parameters["db_label"], version=parameters["version"], - note=parameters["note"], api_key=SecretStr(parameters["api_key"]), index_name=parameters["index_name"], ), diff --git a/vectordb_bench/backend/clients/pinecone/config.py b/vectordb_bench/backend/clients/pinecone/config.py index fe1a039ed..c42c876ff 100644 --- a/vectordb_bench/backend/clients/pinecone/config.py +++ b/vectordb_bench/backend/clients/pinecone/config.py @@ -6,9 +6,11 @@ class PineconeConfig(DBConfig): api_key: SecretStr index_name: str + multitenant_namespace_prefix: str = "vdbbench_mt_" def to_dict(self) -> dict: return { "api_key": self.api_key.get_secret_value(), "index_name": self.index_name, + "multitenant_namespace_prefix": self.multitenant_namespace_prefix, } diff --git a/vectordb_bench/backend/clients/pinecone/pinecone.py b/vectordb_bench/backend/clients/pinecone/pinecone.py index 9c2b38888..f123ae231 100644 --- a/vectordb_bench/backend/clients/pinecone/pinecone.py +++ b/vectordb_bench/backend/clients/pinecone/pinecone.py @@ -1,18 +1,27 @@ """Wrapper around the Pinecone vector database over VectorDB""" import logging +import os +import threading +import time from contextlib import contextmanager +from typing import Any import pinecone from vectordb_bench.backend.filter import Filter, FilterOp +from vectordb_bench.backend.payload import PayloadProfile -from ..api import DBCaseConfig, VectorDB +from ..api import DBCaseConfig, PartialInsertError, VectorDB log = logging.getLogger(__name__) PINECONE_MAX_NUM_PER_BATCH = 1000 PINECONE_MAX_SIZE_PER_BATCH = 2 * 1024 * 1024 # 2MB +PINECONE_QUERY_MAX_RETRIES_ENV = "PINECONE_QUERY_MAX_RETRIES" +PINECONE_QUERY_RETRY_SLEEP_ENV = "PINECONE_QUERY_RETRY_SLEEP_SECONDS" +PINECONE_QUERY_DEFAULT_MAX_RETRIES = 10 +PINECONE_QUERY_DEFAULT_RETRY_SLEEP_SECONDS = 0.5 class Pinecone(VectorDB): @@ -34,15 +43,28 @@ def __init__( """Initialize wrapper around the milvus vector database.""" self.index_name = db_config.get("index_name", "") self.api_key = db_config.get("api_key", "") + self.multitenant_namespace_prefix = db_config.get("multitenant_namespace_prefix", "vdbbench_mt_") + self.multitenant_tenant_labels: list[str] = kwargs.get("multitenant_tenant_labels", []) + self._multitenant_insert_counts: dict[str, int] = {} + self._multitenant_insert_counts_lock = threading.Lock() self.batch_size = int( min(PINECONE_MAX_SIZE_PER_BATCH / (dim * 5), PINECONE_MAX_NUM_PER_BATCH), ) + self._last_write_lsn: int | None = None + self._last_write_lsn_lock = threading.Lock() + self._readiness_probe_vector = [0.0] * dim pc = pinecone.Pinecone(api_key=self.api_key) index = pc.Index(self.index_name) self.with_scalar_labels = with_scalar_labels - if drop_old: + self.expr = None + if drop_old and self.multitenant_tenant_labels: + for tenant in self.multitenant_tenant_labels: + namespace = self._namespace_for_tenant(tenant) + log.info(f"Pinecone index delete multitenant namespace: {namespace}") + index.delete(delete_all=True, namespace=namespace) + elif drop_old: index_stats = index.describe_index_stats() index_dim = index_stats["dimension"] if index_dim != dim: @@ -61,18 +83,154 @@ def init(self): self.index = pc.Index(self.index_name) yield + def __getstate__(self): + state = self.__dict__.copy() + state.pop("_last_write_lsn_lock", None) + state.pop("_multitenant_insert_counts_lock", None) + return state + def optimize(self, data_size: int | None = None): pass + def supports_payload_profile(self, payload_profile: PayloadProfile) -> bool: + return payload_profile in { + PayloadProfile.IDS_ONLY, + PayloadProfile.SCALAR_LABEL, + PayloadProfile.VECTOR, + } + + def supports_multitenant(self) -> bool: + return True + + def set_multitenant_context(self, tenant_labels: list[str]) -> None: + self.multitenant_tenant_labels = tenant_labels + + def _namespace_for_tenant(self, tenant: str | None) -> str | None: + if tenant is None: + return None + return f"{self.multitenant_namespace_prefix}{tenant}" + + def poll_insert_readiness(self, expected_count: int) -> dict: + stats = self.index.describe_index_stats() + if getattr(self, "multitenant_tenant_labels", []): + namespaces = stats.get("namespaces", {}) + expected_by_tenant = self._expected_multitenant_counts(expected_count) + count_ready = True + for tenant, expected_tenant_count in expected_by_tenant.items(): + namespace = self._namespace_for_tenant(tenant) + namespace_stats = namespaces.get(namespace, {}) + namespace_count = namespace_stats.get("vector_count", 0) + count_ready = count_ready and namespace_count >= expected_tenant_count + fresh = self._multitenant_lsn_ready(expected_by_tenant) + return { + "fully_searchable": count_ready and fresh, + "fully_indexed": count_ready and fresh, + "additional_parameters": {}, + } + + count = stats.get("total_vector_count", 0) + count_ready = count >= expected_count + last_write_lsn = getattr(self, "_last_write_lsn", None) + if last_write_lsn is None: + return { + "fully_searchable": count_ready, + "fully_indexed": count_ready, + "additional_parameters": {}, + } + query_res = self.index.query(vector=self._readiness_probe_vector, top_k=1) + indexed_lsn = self._extract_lsn(query_res, "x-pinecone-max-indexed-lsn") + if indexed_lsn is None: + return { + "fully_searchable": count_ready, + "fully_indexed": count_ready, + "additional_parameters": {}, + } + fresh = indexed_lsn >= last_write_lsn + return { + "fully_searchable": count_ready and fresh, + "fully_indexed": count_ready and fresh, + "additional_parameters": {}, + } + + def _expected_multitenant_counts(self, expected_count: int) -> dict[str, int]: + insert_counts = self._multitenant_insert_count_snapshot() + if insert_counts: + return insert_counts + tenant_labels = self.multitenant_tenant_labels + tenant_count = len(tenant_labels) + base_count = expected_count // tenant_count if tenant_count else 0 + remainder = expected_count % tenant_count if tenant_count else 0 + return {tenant: base_count + (1 if idx < remainder else 0) for idx, tenant in enumerate(tenant_labels)} + + def _multitenant_insert_count_snapshot(self) -> dict[str, int]: + if not hasattr(self, "_multitenant_insert_counts_lock"): + self._multitenant_insert_counts_lock = threading.Lock() + with self._multitenant_insert_counts_lock: + return dict(getattr(self, "_multitenant_insert_counts", {})) + + def _record_multitenant_insert_count(self, tenant: str, count: int) -> None: + if not hasattr(self, "_multitenant_insert_counts_lock"): + self._multitenant_insert_counts_lock = threading.Lock() + with self._multitenant_insert_counts_lock: + # Pinecone readiness is count-based per tenant namespace. Unlike + # providers that only track touched tenants, this counter must keep + # every successful write from concurrent insert workers. + self._multitenant_insert_counts[tenant] = self._multitenant_insert_counts.get(tenant, 0) + count + + def _multitenant_lsn_ready(self, expected_by_tenant: dict[str, int]) -> bool: + last_write_lsn = getattr(self, "_last_write_lsn", None) + if last_write_lsn is None: + return True + for tenant, expected_tenant_count in expected_by_tenant.items(): + if expected_tenant_count <= 0: + continue + query_res = self.index.query( + vector=self._readiness_probe_vector, + top_k=1, + namespace=self._namespace_for_tenant(tenant), + ) + indexed_lsn = self._extract_lsn(query_res, "x-pinecone-max-indexed-lsn") + if indexed_lsn is not None and indexed_lsn < last_write_lsn: + return False + return True + + @staticmethod + def _extract_lsn(response: Any, header_name: str) -> int | None: + response_info = getattr(response, "_response_info", None) + if not response_info: + return None + raw_headers = response_info.get("raw_headers", {}) + value = raw_headers.get(header_name.lower()) or raw_headers.get(header_name) + if value is None: + return None + try: + return int(value) + except (TypeError, ValueError): + return None + + def _record_write_lsn(self, write_lsn: int) -> None: + if not hasattr(self, "_last_write_lsn_lock"): + self._last_write_lsn_lock = threading.Lock() + with self._last_write_lsn_lock: + self._last_write_lsn = max(getattr(self, "_last_write_lsn", 0) or 0, write_lsn) + + @staticmethod + def _matches(response: Any) -> list: + if isinstance(response, dict): + return response["matches"] + return response.matches + def insert_embeddings( self, embeddings: list[list[float]], metadata: list[int], labels_data: list[str] | None = None, + tenant_labels_data: list[str] | None = None, **kwargs, ) -> tuple[int, Exception]: assert len(embeddings) == len(metadata) insert_count = 0 + successful_tenants: dict[str, int] = {} try: for batch_start_offset in range(0, len(embeddings), self.batch_size): batch_end_offset = min(batch_start_offset + self.batch_size, len(embeddings)) @@ -87,7 +245,46 @@ def insert_embeddings( metadata_dict, ) insert_datas.append(insert_data) - self.index.upsert(insert_datas) + if tenant_labels_data is None: + upsert_res = self.index.upsert(insert_datas) + write_lsn = self._extract_lsn(upsert_res, "x-pinecone-request-lsn") + if write_lsn is not None: + self._record_write_lsn(write_lsn) + else: + batch_tenant_labels = tenant_labels_data[batch_start_offset:batch_end_offset] + for tenant in sorted(set(batch_tenant_labels)): + tenant_insert_datas = [ + insert_data + for insert_data, tenant_label in zip(insert_datas, batch_tenant_labels, strict=True) + if tenant_label == tenant + ] + try: + upsert_res = self.index.upsert( + tenant_insert_datas, + namespace=self._namespace_for_tenant(tenant), + ) + except Exception as e: + msg = ( + "Pinecone multitenant insert failed for " + f"tenant={tenant} after writing {insert_count} rows; " + f"successful_tenants={successful_tenants}; " + f"failed_tenant_count={len(tenant_insert_datas)}" + ) + return insert_count, PartialInsertError( + msg, + inserted_count=insert_count, + successful_tenants=successful_tenants, + failed_tenant=tenant, + failed_tenant_count=len(tenant_insert_datas), + cause=e, + ) + write_lsn = self._extract_lsn(upsert_res, "x-pinecone-request-lsn") + if write_lsn is not None: + self._record_write_lsn(write_lsn) + insert_count += len(tenant_insert_datas) + successful_tenants[tenant] = successful_tenants.get(tenant, 0) + len(tenant_insert_datas) + self._record_multitenant_insert_count(tenant, len(tenant_insert_datas)) + continue insert_count += batch_end_offset - batch_start_offset except Exception as e: return insert_count, e @@ -98,15 +295,38 @@ def search_embedding( query: list[float], k: int = 100, timeout: int | None = None, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + tenant: str | None = None, ) -> list[int]: pinecone_filters = self.expr - res = self.index.query( - top_k=k, - vector=query, - filter=pinecone_filters, - )["matches"] + include_metadata = payload_profile == PayloadProfile.SCALAR_LABEL + include_values = payload_profile == PayloadProfile.VECTOR + max_retries = int(os.getenv(PINECONE_QUERY_MAX_RETRIES_ENV, PINECONE_QUERY_DEFAULT_MAX_RETRIES)) + retry_sleep = float( + os.getenv(PINECONE_QUERY_RETRY_SLEEP_ENV, PINECONE_QUERY_DEFAULT_RETRY_SLEEP_SECONDS), + ) + for retry_idx in range(max_retries + 1): + try: + query_res = self.index.query( + top_k=k, + vector=query, + filter=pinecone_filters, + include_metadata=include_metadata, + include_values=include_values, + namespace=self._namespace_for_tenant(tenant), + ) + res = self._matches(query_res) + break + except Exception as exc: + if not self._is_rate_limited(exc) or retry_idx >= max_retries: + raise + time.sleep(retry_sleep) return [int(one_res["id"]) for one_res in res] + @staticmethod + def _is_rate_limited(exc: Exception) -> bool: + return getattr(exc, "status", None) == 429 + def prepare_filter(self, filters: Filter): if filters.type == FilterOp.NonFilter: self.expr = None diff --git a/vectordb_bench/backend/clients/pinot/__init__.py b/vectordb_bench/backend/clients/pinot/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vectordb_bench/backend/clients/pinot/cli.py b/vectordb_bench/backend/clients/pinot/cli.py new file mode 100644 index 000000000..e51160b05 --- /dev/null +++ b/vectordb_bench/backend/clients/pinot/cli.py @@ -0,0 +1,202 @@ +from typing import Annotated, TypedDict, Unpack + +import click +from pydantic import SecretStr + +from ....cli.cli import ( + CommonTypedDict, + HNSWFlavor2, + click_parameter_decorators_from_typed_dict, + run, +) +from .. import DB + + +class PinotTypedDict(TypedDict): + controller_host: Annotated[ + str, + click.option("--controller-host", type=str, default="localhost", help="Pinot Controller host"), + ] + controller_port: Annotated[ + int, + click.option("--controller-port", type=int, default=9000, help="Pinot Controller port"), + ] + broker_host: Annotated[ + str, + click.option("--broker-host", type=str, default="localhost", help="Pinot Broker host"), + ] + broker_port: Annotated[ + int, + click.option("--broker-port", type=int, default=8099, help="Pinot Broker port"), + ] + username: Annotated[ + str, + click.option("--username", type=str, default=None, help="Pinot username (optional)"), + ] + password: Annotated[ + str, + click.option("--password", type=str, default=None, help="Pinot password (optional)"), + ] + ingest_batch_size: Annotated[ + int, + click.option( + "--ingest-batch-size", + type=int, + default=100_000, + show_default=True, + help=( + "Rows buffered before flushing one Pinot segment (one ingestFromFile call). " + "Larger values mean fewer segments and better IVF training / query performance. " + "Reduce if memory is constrained (100K x 768-dim float32 ~= 300 MB)." + ), + ), + ] + + +def _pinot_db_config(parameters: dict): + from .config import PinotConfig + + return PinotConfig( + db_label=parameters["db_label"], + controller_host=parameters["controller_host"], + controller_port=parameters["controller_port"], + broker_host=parameters["broker_host"], + broker_port=parameters["broker_port"], + username=parameters.get("username"), + password=SecretStr(parameters["password"]) if parameters.get("password") else None, + ingest_batch_size=parameters["ingest_batch_size"], + ) + + +@click.group() +def Pinot(): + """Apache Pinot vector search benchmarks.""" + + +# --------------------------------------------------------------------------- +# HNSW +# --------------------------------------------------------------------------- + + +class PinotHNSWTypedDict(CommonTypedDict, PinotTypedDict, HNSWFlavor2): ... + + +@Pinot.command("hnsw") +@click_parameter_decorators_from_typed_dict(PinotHNSWTypedDict) +def pinot_hnsw(**parameters: Unpack[PinotHNSWTypedDict]): + from .config import PinotHNSWConfig + + run( + db=DB.Pinot, + db_config=_pinot_db_config(parameters), + db_case_config=PinotHNSWConfig( + m=parameters["m"], + ef_construction=parameters["ef_construction"], + ef=parameters["ef_runtime"], + ), + **parameters, + ) + + +# --------------------------------------------------------------------------- +# IVF_FLAT +# --------------------------------------------------------------------------- + + +class PinotIVFFlatTypedDict(CommonTypedDict, PinotTypedDict): + nlist: Annotated[ + int, + click.option("--nlist", type=int, default=128, help="Number of Voronoi cells (IVF nlist)"), + ] + quantizer: Annotated[ + str, + click.option( + "--quantizer", + type=click.Choice(["FLAT", "SQ8", "SQ4"]), + default="FLAT", + help="Quantizer type for IVF_FLAT", + ), + ] + nprobe: Annotated[ + int, + click.option("--nprobe", type=int, default=8, help="Number of cells to probe at query time"), + ] + train_sample_size: Annotated[ + int, + click.option( + "--train-sample-size", + type=int, + default=None, + help="Training sample size (defaults to max(nlist*50, 1000) if not set)", + ), + ] + + +@Pinot.command("ivf-flat") +@click_parameter_decorators_from_typed_dict(PinotIVFFlatTypedDict) +def pinot_ivf_flat(**parameters: Unpack[PinotIVFFlatTypedDict]): + from .config import PinotIVFFlatConfig + + run( + db=DB.Pinot, + db_config=_pinot_db_config(parameters), + db_case_config=PinotIVFFlatConfig( + nlist=parameters["nlist"], + quantizer=parameters["quantizer"], + nprobe=parameters["nprobe"], + train_sample_size=parameters.get("train_sample_size"), + ), + **parameters, + ) + + +# --------------------------------------------------------------------------- +# IVF_PQ +# --------------------------------------------------------------------------- + + +class PinotIVFPQTypedDict(CommonTypedDict, PinotTypedDict): + nlist: Annotated[ + int, + click.option("--nlist", type=int, default=128, help="Number of Voronoi cells (IVF nlist)"), + ] + pq_m: Annotated[ + int, + click.option("--pq-m", type=int, default=8, help="Number of PQ sub-quantizers (must divide dimension)"), + ] + pq_nbits: Annotated[ + int, + click.option( + "--pq-nbits", + type=click.Choice(["4", "6", "8"]), + default="8", + help="Bits per PQ code (4, 6, or 8)", + ), + ] + train_sample_size: Annotated[ + int, + click.option("--train-sample-size", type=int, default=6400, help="Training sample size (must be >= nlist)"), + ] + nprobe: Annotated[ + int, + click.option("--nprobe", type=int, default=8, help="Number of cells to probe at query time"), + ] + + +@Pinot.command("ivf-pq") +@click_parameter_decorators_from_typed_dict(PinotIVFPQTypedDict) +def pinot_ivf_pq(**parameters: Unpack[PinotIVFPQTypedDict]): + from .config import PinotIVFPQConfig + + run( + db=DB.Pinot, + db_config=_pinot_db_config(parameters), + db_case_config=PinotIVFPQConfig( + nlist=parameters["nlist"], + pq_m=parameters["pq_m"], + pq_nbits=int(parameters["pq_nbits"]), + train_sample_size=parameters["train_sample_size"], + nprobe=parameters["nprobe"], + ), + **parameters, + ) diff --git a/vectordb_bench/backend/clients/pinot/config.py b/vectordb_bench/backend/clients/pinot/config.py new file mode 100644 index 000000000..e29db0bec --- /dev/null +++ b/vectordb_bench/backend/clients/pinot/config.py @@ -0,0 +1,94 @@ +from pydantic import BaseModel, SecretStr + +from ..api import DBCaseConfig, DBConfig, MetricType + + +class PinotConfig(DBConfig): + controller_host: str = "localhost" + controller_port: int = 9000 + broker_host: str = "localhost" + broker_port: int = 8099 + username: str | None = None + password: SecretStr | None = None + # Rows buffered before flushing one Pinot segment (one ingestFromFile call). + # Larger values → fewer segments → better IVF training & query perf. + # 100_000 rows x 768-dim float32 ~= 300 MB in-memory. + ingest_batch_size: int = 100_000 + + def to_dict(self) -> dict: + return { + "controller_host": self.controller_host, + "controller_port": self.controller_port, + "broker_host": self.broker_host, + "broker_port": self.broker_port, + "username": self.username, + "password": self.password.get_secret_value() if self.password else None, + "ingest_batch_size": self.ingest_batch_size, + } + + +class PinotHNSWConfig(BaseModel, DBCaseConfig): + """HNSW vector index config for Apache Pinot (Lucene-based).""" + + metric_type: MetricType | None = None + m: int = 16 # maxCon: max connections per node + ef_construction: int = 100 # beamWidth: construction beam width + ef: int | None = None # ef_search: HNSW candidate list size at query time (default=k) + + def index_param(self) -> dict: + return { + "vectorIndexType": "HNSW", + "maxCon": str(self.m), + "beamWidth": str(self.ef_construction), + } + + def search_param(self) -> dict: + # ef controls the HNSW candidate list during search via vectorSimilarity(col, q, ef). + # Larger ef → better recall, slightly higher latency. Defaults to k if not set. + return {"ef": self.ef} if self.ef is not None else {} + + +class PinotIVFFlatConfig(BaseModel, DBCaseConfig): + """IVF_FLAT vector index config for Apache Pinot.""" + + metric_type: MetricType | None = None + nlist: int = 128 # number of Voronoi cells (centroids) + quantizer: str = "FLAT" # FLAT, SQ8, or SQ4 + train_sample_size: int | None = None # defaults to max(nlist*50, 1000) if None + nprobe: int = 8 # number of cells to probe at query time + + def index_param(self) -> dict: + params: dict = { + "vectorIndexType": "IVF_FLAT", + "nlist": str(self.nlist), + "quantizer": self.quantizer, + } + if self.train_sample_size is not None: + params["trainSampleSize"] = str(self.train_sample_size) + return params + + def search_param(self) -> dict: + return {"nprobe": self.nprobe} + + +class PinotIVFPQConfig(BaseModel, DBCaseConfig): + """IVF_PQ vector index config for Apache Pinot (residual product quantization).""" + + metric_type: MetricType | None = None + nlist: int = 128 # number of Voronoi cells (centroids) + pq_m: int = 8 # number of sub-quantizers (must divide vectorDimension) + pq_nbits: int = 8 # bits per sub-quantizer code: 4, 6, or 8 + train_sample_size: int = 6400 # training sample size (must be >= nlist) + nprobe: int = 8 # number of cells to probe at query time + + def index_param(self) -> dict: + return { + "vectorIndexType": "IVF_PQ", + "nlist": str(self.nlist), + "pqM": str(self.pq_m), + "pqNbits": str(self.pq_nbits), + "trainSampleSize": str(self.train_sample_size), + } + + def search_param(self) -> dict: + return {"nprobe": self.nprobe} diff --git a/vectordb_bench/backend/clients/pinot/pinot.py b/vectordb_bench/backend/clients/pinot/pinot.py new file mode 100644 index 000000000..bdae58d64 --- /dev/null +++ b/vectordb_bench/backend/clients/pinot/pinot.py @@ -0,0 +1,449 @@ +"""Wrapper around Apache Pinot vector database over VectorDB""" + +import json +import logging +import tempfile +import threading +import time +from contextlib import contextmanager +from pathlib import Path +from typing import Any + +import requests + +from ...filter import Filter, FilterOp +from ..api import DBCaseConfig, MetricType, VectorDB + +log = logging.getLogger(__name__) + +# Rows accumulated before flushing a segment to Pinot. +# Large enough to avoid thousands of tiny segments (which breaks IVF training +# and hurts query performance), yet small enough to keep memory use bounded. +# For 768-dim float32 vectors: 100K rows ≈ 300 MB in-memory. +DEFAULT_INGEST_BATCH_SIZE = 100_000 + + +class Pinot(VectorDB): + """Apache Pinot vector database client for VectorDBBench.""" + + name = "Pinot" + # thread_safe=True: each flush uses a fresh requests.Session (not a shared one), + # and each worker thread has its own row buffer via threading.local(). + # This lets the framework spawn multiple load workers that flush segments in parallel. + thread_safe: bool = True + supported_filter_types: list[FilterOp] = [ + FilterOp.NonFilter, + FilterOp.NumGE, + FilterOp.StrEqual, + ] + + def __init__( + self, + dim: int, + db_config: dict, + db_case_config: DBCaseConfig, + collection_name: str = "VectorBenchCollection", + drop_old: bool = False, + with_scalar_labels: bool = False, + **kwargs, + ): + self.dim = dim + self.case_config = db_case_config + self.table_name = collection_name + self._primary_field = "id" + self._vector_field = "embedding" + self._label_field = "labels" + self.with_scalar_labels = with_scalar_labels + self._filter_where: str = "" # set by prepare_filter(); applied in search_embedding() + + controller_host = db_config["controller_host"] + controller_port = db_config["controller_port"] + broker_host = db_config["broker_host"] + broker_port = db_config["broker_port"] + self._controller_url = f"http://{controller_host}:{controller_port}" + self._broker_url = f"http://{broker_host}:{broker_port}" + + self._auth = None + if db_config.get("username") and db_config.get("password"): + self._auth = (db_config["username"], db_config["password"]) + + # Per-thread row buffers — each worker thread accumulates rows independently + # and flushes its own segment when the threshold is reached. + # _registered_buffers tracks all thread-local objects so init() teardown + # can flush any remaining rows from every worker thread. + self._thread_local = threading.local() + self._registered_buffers: list = [] + self._buffers_lock = threading.Lock() + self._ingest_batch_size: int = db_config.get("ingest_batch_size", DEFAULT_INGEST_BATCH_SIZE) + + self.session = None + + with requests.Session() as setup_session: + if self._auth: + setup_session.auth = self._auth + + if drop_old: + self._delete_table(setup_session) + self._delete_schema(setup_session) + + if not self._schema_exists(setup_session): + self._create_schema(setup_session) + + if not self._table_exists(setup_session): + self._create_table(setup_session) + + def _schema_exists(self, session: requests.Session) -> bool: + resp = session.get(f"{self._controller_url}/schemas/{self.table_name}") + return resp.status_code == 200 + + def _table_exists(self, session: requests.Session) -> bool: + resp = session.get(f"{self._controller_url}/tables/{self.table_name}") + if resp.status_code != 200: + return False + data = resp.json() + return bool(data.get("OFFLINE") or data.get("tables")) + + def _delete_table(self, session: requests.Session): + resp = session.delete(f"{self._controller_url}/tables/{self.table_name}?type=offline") + if resp.status_code not in (200, 404): + log.warning(f"Failed to delete Pinot table {self.table_name}: {resp.text}") + else: + log.info(f"Deleted Pinot table: {self.table_name}") + # Wait for Pinot to finish cleaning up the external view + for _ in range(30): + check = session.get(f"{self._controller_url}/tables/{self.table_name}/externalview") + if check.status_code == 404 or not check.json(): + break + log.info(f"Waiting for Pinot external view cleanup for {self.table_name}...") + time.sleep(2) + else: + log.warning(f"External view for {self.table_name} did not clear within 60s") + + def _delete_schema(self, session: requests.Session): + resp = session.delete(f"{self._controller_url}/schemas/{self.table_name}") + if resp.status_code not in (200, 404): + log.warning(f"Failed to delete Pinot schema {self.table_name}: {resp.text}") + else: + log.info(f"Deleted Pinot schema: {self.table_name}") + + def _create_schema(self, session: requests.Session): + dimension_fields = [ + {"name": self._primary_field, "dataType": "INT"}, + {"name": self._vector_field, "dataType": "FLOAT", "singleValueField": False}, + ] + if self.with_scalar_labels: + dimension_fields.append({"name": self._label_field, "dataType": "STRING"}) + + schema = { + "schemaName": self.table_name, + "dimensionFieldSpecs": dimension_fields, + } + resp = session.post( + f"{self._controller_url}/schemas", + json=schema, + headers={"Content-Type": "application/json"}, + ) + if not resp.ok: + log.error(f"Failed to create Pinot schema: {resp.text}") + resp.raise_for_status() + log.info(f"Created Pinot schema: {self.table_name}") + + def _create_table(self, session: requests.Session): + metric_str = self._get_index_metric_str() + index_params = self.case_config.index_param() + + # Pull vectorIndexType out of index_params; remaining entries are type-specific properties. + vector_index_type = index_params.pop("vectorIndexType", "HNSW") + properties: dict = { + "vectorIndexType": vector_index_type, + "vectorDimension": str(self.dim), + "vectorDistanceFunction": metric_str, + "version": "1", + } + properties.update(index_params) + + table_config = { + "tableName": self.table_name, + "tableType": "OFFLINE", + "segmentsConfig": { + "replication": "1", + "schemaName": self.table_name, + }, + "tenants": {}, + "tableIndexConfig": { + "loadMode": "MMAP", + # Inverted index on id for fast equality lookups; range index for >=/<= filters. + "invertedIndexColumns": [self._primary_field], + "rangeIndexColumns": [self._primary_field], + }, + "fieldConfigList": [ + { + "encodingType": "RAW", + "indexType": "VECTOR", + "name": self._vector_field, + "properties": properties, + } + ], + "ingestionConfig": { + "batchIngestionConfig": { + "segmentIngestionType": "APPEND", + "segmentIngestionFrequency": "DAILY", + } + }, + "metadata": {}, + } + resp = session.post( + f"{self._controller_url}/tables", + json=table_config, + headers={"Content-Type": "application/json"}, + ) + if not resp.ok: + log.error(f"Failed to create Pinot table: {resp.text}") + resp.raise_for_status() + log.info(f"Created Pinot table: {self.table_name}") + + def _get_index_metric_str(self) -> str: + if self.case_config.metric_type == MetricType.COSINE: + return "COSINE" + if self.case_config.metric_type == MetricType.IP: + return "INNER_PRODUCT" + return "L2" + + def _get_query_distance_fn(self) -> tuple[str, str]: + """Returns (sql_function_name, sort_order) for vector search.""" + if self.case_config.metric_type == MetricType.COSINE: + return "cosineDistance", "ASC" + if self.case_config.metric_type == MetricType.IP: + return "innerProduct", "DESC" + return "l2Distance", "ASC" + + def prepare_filter(self, filters: Filter): + """Pre-compute the SQL WHERE fragment for the given filter condition.""" + if filters.type == FilterOp.NonFilter: + self._filter_where = "" + elif filters.type == FilterOp.NumGE: + self._filter_where = f"{filters.int_field} >= {filters.int_value}" + elif filters.type == FilterOp.StrEqual: + self._filter_where = f"{self._label_field} = '{filters.label_value}'" + + def __getstate__(self): + # threading.local and Lock cannot be pickled; the framework pickles the DB + # instance to send it to the load subprocess, so we must exclude them here + # and recreate them in __setstate__ after unpickling. + state = self.__dict__.copy() + state.pop("_thread_local", None) + state.pop("_buffers_lock", None) + state.pop("_registered_buffers", None) + return state + + def __setstate__(self, state: dict) -> None: + self.__dict__.update(state) + self._thread_local = threading.local() + self._buffers_lock = threading.Lock() + self._registered_buffers = [] + + def _get_thread_buffer(self) -> list: + """Return this thread's row buffer, creating and registering it on first access. + + The list itself (not the threading.local container) is registered so that the + teardown in init() can access buffered rows from the main thread, which has its + own (empty) thread-local slot and cannot see other threads' data through the + threading.local object. + """ + if not hasattr(self._thread_local, "pending_rows"): + self._thread_local.pending_rows = [] + with self._buffers_lock: + # Register the list itself, not self._thread_local, so teardown can + # read the contents from any thread (main thread included). + self._registered_buffers.append(self._thread_local.pending_rows) + return self._thread_local.pending_rows + + @contextmanager + def init(self): + self.session = requests.Session() + if self._auth: + self.session.auth = self._auth + # Reset buffer registry so teardown only flushes buffers from this init() scope. + self._registered_buffers = [] + try: + yield + finally: + # Flush any rows that were buffered but not yet sent to Pinot. + # Each worker thread may have its own partially-filled buffer. + # This must happen here — not in optimize() — because optimize() runs + # in a separate subprocess where the buffers are always empty. + for pending in self._registered_buffers: + if pending: + log.info(f"Pinot init teardown: flushing {len(pending)} remaining buffered rows") + _, err = self._flush_rows(pending) + if err: + log.warning(f"Pinot init teardown: flush error: {err}") + self.session.close() + self.session = None + + def _flush_rows(self, rows: list) -> tuple[int, Exception | None]: + """Flush the given row list to Pinot as one segment using a fresh HTTP session. + + Using a fresh session (not self.session) makes this method safe to call + from multiple threads concurrently. On success the list is cleared in-place. + Returns (rows_flushed, error). On error the list is left intact so the caller + can decide whether to retry. + """ + if not rows: + return 0, None + + n = len(rows) + with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False, prefix="pinot_ingest_") as f: + for row in rows: + f.write(json.dumps(row) + "\n") + tmp_path = f.name + + try: + batch_config = json.dumps({"inputFormat": "json"}) + params = { + "tableNameWithType": f"{self.table_name}_OFFLINE", + "batchConfigMapStr": batch_config, + } + last_err = None + with requests.Session() as session: + if self._auth: + session.auth = self._auth + for attempt in range(3): + try: + with Path(tmp_path).open("rb") as f: + resp = session.post( + f"{self._controller_url}/ingestFromFile", + params=params, + files={"file": (Path(tmp_path).name, f, "application/json")}, + timeout=1800, # HNSW index building for 100K x 768D can take 10+ min + ) + if resp.ok: + rows.clear() + log.debug(f"Pinot: flushed segment with {n} rows") + return n, None + last_err = Exception(f"HTTP {resp.status_code}: {resp.text[:200]}") + log.warning(f"Pinot flush attempt {attempt + 1} failed: {last_err}") + time.sleep(1 + attempt) + except Exception as e: + last_err = e + log.warning(f"Pinot flush attempt {attempt + 1} error: {e}") + time.sleep(1 + attempt) + return 0, last_err + finally: + Path(tmp_path).unlink() + + def insert_embeddings( + self, + embeddings: list[list[float]], + metadata: list[int], + labels_data: list[str] | None = None, + **kwargs: Any, + ) -> tuple[int, Exception]: + # Each thread has its own buffer; no locking needed here. + pending = self._get_thread_buffer() + + for i, (emb, meta) in enumerate(zip(embeddings, metadata, strict=False)): + row = {self._primary_field: meta, self._vector_field: list(emb)} + if self.with_scalar_labels and labels_data is not None: + row[self._label_field] = labels_data[i] + pending.append(row) + + if len(pending) >= self._ingest_batch_size: + _flushed, err = self._flush_rows(pending) + if err: + log.warning(f"Failed to flush Pinot buffer: {err}") + return 0, err + + return len(embeddings), None + + def search_embedding( + self, + query: list[float], + k: int = 100, + filters: dict | None = None, + timeout: int | None = None, + ) -> list[int]: + assert self.session is not None, "Session not initialized" + + query_arr = ",".join(str(v) for v in query) + dist_fn, order = self._get_query_distance_fn() + + search_params = self.case_config.search_param() + nprobe = search_params.get("nprobe") + + filter_clause = self._filter_where + + if nprobe is not None: + # IVF-based index: set probe count via session option, use ORDER BY for top-k + where = f"WHERE {filter_clause} " if filter_clause else "" + sql = ( + f"set vectorNprobe={nprobe}; " + f"SELECT {self._primary_field} " + f"FROM {self.table_name} " + f"{where}" + f"ORDER BY {dist_fn}({self._vector_field}, ARRAY[{query_arr}]) {order} " + f"LIMIT {k}" + ) + else: + # HNSW index: WHERE vectorSimilarity(..., ef) triggers Lucene HNSW graph search + # with a candidate list of size ef (defaults to k). Larger ef → better recall. + # ORDER BY dist re-ranks the ANN candidates for correct final recall. + ef = search_params.get("ef") or k + extra = f" AND {filter_clause}" if filter_clause else "" + sql = ( + f"SELECT {self._primary_field} " + f"FROM {self.table_name} " + f"WHERE vectorSimilarity({self._vector_field}, ARRAY[{query_arr}], {ef}){extra} " + f"ORDER BY {dist_fn}({self._vector_field}, ARRAY[{query_arr}]) {order} " + f"LIMIT {k}" + ) + + resp = self.session.post( + f"{self._broker_url}/query/sql", + json={"sql": sql}, + headers={"Content-Type": "application/json"}, + timeout=timeout, + ) + resp.raise_for_status() + result = resp.json() + + rows = result.get("resultTable", {}).get("rows", []) + return [row[0] for row in rows] + + def optimize(self, data_size: int | None = None): + """Wait for all ingested data to be queryable in Pinot. + + Remaining buffered rows are flushed on init() teardown (in the insert + subprocess), so by the time optimize() runs they are already in Pinot. + """ + if self.session is None: + return + + if data_size is None: + time.sleep(5) + return + + max_wait = 600 + check_interval = 10 + start = time.time() + + while time.time() - start < max_wait: + try: + resp = self.session.post( + f"{self._broker_url}/query/sql", + json={"sql": f"SELECT COUNT(*) FROM {self.table_name}"}, + headers={"Content-Type": "application/json"}, + ) + if resp.status_code == 200: + rows = resp.json().get("resultTable", {}).get("rows", []) + current_count = rows[0][0] if rows else 0 + if current_count >= data_size: + log.info(f"Pinot: all {data_size} rows are queryable") + return + log.info(f"Pinot: {current_count}/{data_size} rows queryable, waiting...") + except Exception as e: + log.warning(f"Pinot optimize check error: {e}") + + time.sleep(check_interval) + + log.warning(f"Pinot optimize timed out after {max_wait}s") diff --git a/vectordb_bench/backend/clients/polardb/__init__.py b/vectordb_bench/backend/clients/polardb/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vectordb_bench/backend/clients/polardb/cli.py b/vectordb_bench/backend/clients/polardb/cli.py new file mode 100644 index 000000000..6f7a8f79c --- /dev/null +++ b/vectordb_bench/backend/clients/polardb/cli.py @@ -0,0 +1,248 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Annotated, Unpack + +if TYPE_CHECKING: + from .config import PolarDBConfig + +import click +from pydantic import SecretStr + +from vectordb_bench.backend.clients import DB + +from ....cli.cli import ( + CommonTypedDict, + cli, + click_parameter_decorators_from_typed_dict, + run, +) + + +class PolarDBTypedDict(CommonTypedDict): + user_name: Annotated[ + str, + click.option( + "--username", + type=str, + help="Username", + required=True, + ), + ] + password: Annotated[ + str, + click.option( + "--password", + type=str, + help="Password", + default="", + ), + ] + + host: Annotated[ + str, + click.option( + "--host", + type=str, + help="Db host", + default="127.0.0.1", + ), + ] + + port: Annotated[ + int, + click.option( + "--port", + type=int, + default=3306, + help="Db Port", + ), + ] + + database: Annotated[ + str, + click.option( + "--database", + type=str, + help="Database name", + default="vectordbbench", + ), + ] + + unix_socket: Annotated[ + str, + click.option( + "--unix-socket", + type=str, + help="Unix socket path (overrides host/port if set)", + default="", + ), + ] + + +class PolarDBHNSWTypedDict(PolarDBTypedDict): + m: Annotated[ + int, + click.option( + "--m", + type=int, + help="M parameter (max_degree) in HNSW", + default=16, + ), + ] + + ef_construction: Annotated[ + int, + click.option( + "--ef-construction", + type=int, + help="ef_construction parameter in HNSW", + default=200, + ), + ] + + ef_search: Annotated[ + int, + click.option( + "--ef-search", + type=int, + help="polar_vector_index_hnsw_ef_search session variable", + default=64, + ), + ] + + insert_workers: Annotated[ + int, + click.option( + "--insert-workers", + type=int, + help="Number of concurrent threads for data insertion", + default=10, + ), + ] + + post_load_index: Annotated[ + bool, + click.option( + "--post-load-index/--inline-index", + type=bool, + help="Create vector index via ALTER TABLE after data load; " + "otherwise create index inline during table creation", + default=False, + ), + ] + + +class PolarDBHNSWPQTypedDict(PolarDBHNSWTypedDict): + pq_m: Annotated[ + int, + click.option( + "--pq-m", + type=int, + help="PQ subquantizer count (must divide dimension)", + default=1, + ), + ] + + pq_nbits: Annotated[ + int, + click.option( + "--pq-nbits", + type=int, + help="PQ bits per subquantizer (max 24)", + default=8, + ), + ] + + +class PolarDBHNSWSQTypedDict(PolarDBHNSWTypedDict): + sq_type: Annotated[ + str, + click.option( + "--sq-type", + type=str, + help="SQ quantizer type (8bit, 4bit, fp16, bf16, 6bit, etc.)", + default="8bit", + ), + ] + + +def _build_db_config(parameters: dict) -> PolarDBConfig: + from .config import PolarDBConfig + + pwd = parameters["password"] + sock = parameters["unix_socket"] + return PolarDBConfig( + db_label=parameters["db_label"], + user_name=parameters["username"], + password=SecretStr(pwd) if pwd else None, + host=parameters["host"], + port=parameters["port"], + database=parameters["database"], + unix_socket=sock if sock else None, + ) + + +@cli.command() +@click_parameter_decorators_from_typed_dict(PolarDBHNSWTypedDict) +def PolarDBHNSWFlat( + **parameters: Unpack[PolarDBHNSWTypedDict], +): + from .config import PolarDBHNSWFlatConfig + + run( + db=DB.PolarDB, + db_config=_build_db_config(parameters), + db_case_config=PolarDBHNSWFlatConfig( + M=parameters["m"], + ef_construction=parameters["ef_construction"], + ef_search=parameters["ef_search"], + insert_workers=parameters["insert_workers"], + post_load_index=parameters["post_load_index"], + ), + **parameters, + ) + + +@cli.command() +@click_parameter_decorators_from_typed_dict(PolarDBHNSWPQTypedDict) +def PolarDBHNSWPQ( + **parameters: Unpack[PolarDBHNSWPQTypedDict], +): + from .config import PolarDBHNSWPQConfig + + run( + db=DB.PolarDB, + db_config=_build_db_config(parameters), + db_case_config=PolarDBHNSWPQConfig( + M=parameters["m"], + ef_construction=parameters["ef_construction"], + ef_search=parameters["ef_search"], + insert_workers=parameters["insert_workers"], + post_load_index=parameters["post_load_index"], + pq_m=parameters["pq_m"], + pq_nbits=parameters["pq_nbits"], + ), + **parameters, + ) + + +@cli.command() +@click_parameter_decorators_from_typed_dict(PolarDBHNSWSQTypedDict) +def PolarDBHNSWSQ( + **parameters: Unpack[PolarDBHNSWSQTypedDict], +): + from .config import PolarDBHNSWSQConfig + + run( + db=DB.PolarDB, + db_config=_build_db_config(parameters), + db_case_config=PolarDBHNSWSQConfig( + M=parameters["m"], + ef_construction=parameters["ef_construction"], + ef_search=parameters["ef_search"], + insert_workers=parameters["insert_workers"], + post_load_index=parameters["post_load_index"], + sq_type=parameters["sq_type"], + ), + **parameters, + ) diff --git a/vectordb_bench/backend/clients/polardb/config.py b/vectordb_bench/backend/clients/polardb/config.py new file mode 100644 index 000000000..5121c1e19 --- /dev/null +++ b/vectordb_bench/backend/clients/polardb/config.py @@ -0,0 +1,148 @@ +from typing import TypedDict + +from pydantic import BaseModel, SecretStr + +from ..api import DBCaseConfig, DBConfig, IndexType, MetricType + + +class PolarDBConfigDict(TypedDict): + user: str + password: str + host: str + port: int + database: str + unix_socket: str | None = None + + +class PolarDBConfig(DBConfig): + user_name: str = "root" + password: SecretStr | None = None + host: str = "127.0.0.1" + port: int = 3306 + database: str = "vectordbbench" + unix_socket: str | None = None + + @staticmethod + def common_long_configs() -> list[str]: + return ["note", "unix_socket"] + + def to_dict(self) -> PolarDBConfigDict: + pwd_str = self.password.get_secret_value() if self.password else "" + return { + "host": self.host, + "port": self.port, + "user": self.user_name, + "password": pwd_str, + "database": self.database, + "unix_socket": self.unix_socket or None, + } + + +class PolarDBIndexConfig(BaseModel): + """Base config for PolarDB vector index""" + + metric_type: MetricType | None = None + + def parse_metric(self) -> str: + if self.metric_type == MetricType.L2: + return "EUCLIDEAN" + if self.metric_type == MetricType.COSINE: + return "COSINE" + if self.metric_type == MetricType.IP: + return "INNER_PRODUCT" + msg = f"Metric type {self.metric_type} is not supported!" + raise ValueError(msg) + + def parse_metric_for_distance(self) -> str: + """Return the metric name used in DISTANCE() function""" + if self.metric_type == MetricType.L2: + return "EUCLIDEAN" + if self.metric_type == MetricType.COSINE: + return "COSINE" + if self.metric_type == MetricType.IP: + return "DOT" + msg = f"Metric type {self.metric_type} is not supported!" + raise ValueError(msg) + + +class PolarDBHNSWBaseConfig(PolarDBIndexConfig, DBCaseConfig): + """Shared HNSW config fields for all PolarDB HNSW variants.""" + + M: int = 16 + ef_construction: int = 200 + ef_search: int = 64 + insert_workers: int = 10 + post_load_index: bool = False # If True, create index after data load via ALTER TABLE + index: IndexType = IndexType.HNSW + + def search_param(self) -> dict: + return { + "metric_type": self.parse_metric_for_distance(), + "ef_search": self.ef_search, + } + + +class PolarDBHNSWFlatConfig(PolarDBHNSWBaseConfig): + def index_param(self) -> dict: + return { + "metric_type": self.parse_metric(), + "metric_type_distance": self.parse_metric_for_distance(), + "index_type": "FAISS_HNSW_FLAT", + "M": self.M, + "ef_construction": self.ef_construction, + "vector_index_comment": ( + f"imci_vector_index=FAISS_HNSW_FLAT(" + f"metric={self.parse_metric()}," + f"max_degree={self.M}," + f"ef_construction={self.ef_construction})" + ), + } + + +class PolarDBHNSWPQConfig(PolarDBHNSWBaseConfig): + pq_m: int = 1 + pq_nbits: int = 8 + + def index_param(self) -> dict: + return { + "metric_type": self.parse_metric(), + "metric_type_distance": self.parse_metric_for_distance(), + "index_type": "FAISS_HNSW_PQ", + "M": self.M, + "ef_construction": self.ef_construction, + "vector_index_comment": ( + f"imci_vector_index=FAISS_HNSW_PQ(" + f"metric={self.parse_metric()}," + f"max_degree={self.M}," + f"ef_construction={self.ef_construction}," + f"pq_m={self.pq_m}," + f"pq_nbits={self.pq_nbits})" + ), + } + + +class PolarDBHNSWSQConfig(PolarDBHNSWBaseConfig): + sq_type: str = "8bit" + + def index_param(self) -> dict: + return { + "metric_type": self.parse_metric(), + "metric_type_distance": self.parse_metric_for_distance(), + "index_type": "FAISS_HNSW_SQ", + "M": self.M, + "ef_construction": self.ef_construction, + "vector_index_comment": ( + f"imci_vector_index=FAISS_HNSW_SQ(" + f"metric={self.parse_metric()}," + f"max_degree={self.M}," + f"ef_construction={self.ef_construction}," + f"sq_type={self.sq_type})" + ), + } + + +_polardb_case_config = { + IndexType.HNSW: PolarDBHNSWFlatConfig, + IndexType.HNSW_PQ: PolarDBHNSWPQConfig, + IndexType.HNSW_SQ: PolarDBHNSWSQConfig, +} diff --git a/vectordb_bench/backend/clients/polardb/polardb.py b/vectordb_bench/backend/clients/polardb/polardb.py new file mode 100644 index 000000000..d53638b0e --- /dev/null +++ b/vectordb_bench/backend/clients/polardb/polardb.py @@ -0,0 +1,286 @@ +import concurrent.futures +import logging +import time +from contextlib import contextmanager + +import numpy as np +import pymysql + +from ..api import VectorDB +from .config import PolarDBConfigDict, PolarDBIndexConfig + +log = logging.getLogger(__name__) + + +class PolarDB(VectorDB): + def __init__( + self, + dim: int, + db_config: PolarDBConfigDict, + db_case_config: PolarDBIndexConfig, + collection_name: str = "vec_collection", + drop_old: bool = False, + **kwargs, + ): + self.name = "PolarDB" + self.db_config = db_config + self.case_config = db_case_config + self.table_name = collection_name + self.dim = dim + + conn, cursor = self._create_connection() + + if drop_old: + log.info(f"PolarDB dropping old table: {self.table_name}") + self._create_db_table(cursor, dim) + + cursor.close() + conn.close() + + def _create_connection(self): + connect_kwargs = { + "user": self.db_config["user"], + "password": self.db_config["password"], + "autocommit": True, + } + if self.db_config.get("unix_socket"): + connect_kwargs["unix_socket"] = self.db_config["unix_socket"] + else: + connect_kwargs["host"] = self.db_config["host"] + connect_kwargs["port"] = self.db_config["port"] + + conn = pymysql.connect(**connect_kwargs) + cursor = conn.cursor() + # Disable query cache to ensure accurate benchmarking + cursor.execute("SET query_cache_type = OFF") + return conn, cursor + + def _create_db_table(self, cursor: pymysql.cursors.Cursor, dim: int) -> None: + index_param = self.case_config.index_param() + vector_index_comment = index_param["vector_index_comment"] + post_load_index = getattr(self.case_config, "post_load_index", False) + + try: + log.info(f"PolarDB creating database: {self.db_config['database']}") + cursor.execute(f"CREATE DATABASE IF NOT EXISTS {self.db_config['database']}") + cursor.execute(f"USE {self.db_config['database']}") + cursor.execute(f"DROP TABLE IF EXISTS {self.table_name}") + + if post_load_index: + # Post-load mode: create table without vector index, will add via ALTER TABLE later + create_sql = ( + f"CREATE TABLE {self.table_name} (" + f"id INT PRIMARY KEY, " + f"v VECTOR({dim}) NOT NULL" + f") ENGINE=InnoDB COMMENT 'COLUMNAR=1'" + ) + log.info(f"PolarDB creating table (post-load index mode): {create_sql}") + else: + # Inline mode: create table with vector index comment + create_sql = ( + f"CREATE TABLE {self.table_name} (" + f"id INT PRIMARY KEY, " + f'v VECTOR({dim}) NOT NULL COMMENT "{vector_index_comment}"' + f") ENGINE=InnoDB COMMENT 'COLUMNAR=1'" + ) + log.info(f"PolarDB creating table: {create_sql}") + cursor.execute(create_sql) + except Exception as e: + log.warning(f"Failed to create table: {self.table_name} error: {e}") + raise + + @contextmanager + def init(self): + self.conn, self.cursor = self._create_connection() + + search_param = self.case_config.search_param() + + # Force PolarDB vector search to use the IMCI engine. + self.cursor.execute("SET use_imci_engine = FORCED") + self.cursor.execute("SET imci_enable_vector_search = ON") + self.cursor.execute("SET imci_max_dop = 1") + self.cursor.execute("SET cost_threshold_for_imci = 0") + + # Set ef_search + if search_param.get("ef_search") is not None: + self.cursor.execute(f"SET polar_vector_index_hnsw_ef_search = {search_param['ef_search']}") + + metric_type = search_param["metric_type"] + db_name = self.db_config["database"] + hint = "/*+ SET_VAR(imci_enable_fast_vector_search=on) */" + + self.insert_sql = f"INSERT INTO {db_name}.{self.table_name} (id, v) VALUES (%s, _binary %s)" + self.select_sql = ( + f"SELECT {hint} id FROM {db_name}.{self.table_name} " + f"ORDER BY DISTANCE(v, _binary %s, '{metric_type}') " + f"LIMIT %s" + ) + self.select_sql_with_filter = ( + f"SELECT id FROM {db_name}.{self.table_name} " + f"WHERE id >= %s " + f"ORDER BY DISTANCE(v, _binary %s, '{metric_type}') " + f"LIMIT %s" + ) + + try: + yield + finally: + self.cursor.close() + self.conn.close() + self.cursor = None + self.conn = None + + def ready_to_load(self) -> bool: + pass + + def optimize(self, data_size: int | None = None) -> None: + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + + db_name = self.db_config["database"] + index_param = self.case_config.index_param() + post_load_index = getattr(self.case_config, "post_load_index", False) + + # Ensure vector index builds even for small datasets + try: + self.cursor.execute("SET GLOBAL imci_vector_index_dump_rows_threshold = 1") + except Exception as e: + log.warning(f"Cannot SET GLOBAL imci_vector_index_dump_rows_threshold (need SUPER): {e}") + + start_time = time.time() + + if post_load_index: + # Post-load mode: issue ALTER TABLE to add vector index after data load + vector_index_comment = index_param["vector_index_comment"] + alter_sql = ( + f"ALTER TABLE {db_name}.{self.table_name} " + f'MODIFY COLUMN v VECTOR({self.dim}) NOT NULL COMMENT "{vector_index_comment}"' + ) + log.info(f"PolarDB creating vector index via ALTER TABLE: {alter_sql}") + self.cursor.execute(alter_sql) + log.info("ALTER TABLE completed.") + + analyze_sql = f"/* FORCE_IMCI_NODES */ ANALYZE TABLE {db_name}.{self.table_name}" + log.info(f"PolarDB analyzing table: {analyze_sql}") + analyze_start = time.time() + self.cursor.execute(analyze_sql) + analyze_elapsed = time.time() - analyze_start + + log.info(f"ANALYZE TABLE completed in {analyze_elapsed:.1f}s, waiting for vector index to be built...") + + last_vectors = 0 + while True: + self.cursor.execute( + "SELECT VECTORS FROM information_schema.imci_vector_index_stats " + "WHERE SCHEMA_NAME=%s AND TABLE_NAME=%s", + (db_name, self.table_name), + ) + result = self.cursor.fetchone() + + if result is None: + log.info("Vector index stats not yet available, waiting...") + time.sleep(2) + continue + + vectors = int(result[0]) + + if vectors != last_vectors: + elapsed = max(0.0, time.time() - start_time - analyze_elapsed) + log.info( + f"Vector index building: {vectors} vectors indexed " + f"(target: {data_size}), elapsed: {elapsed:.1f}s" + ) + last_vectors = vectors + + if data_size is not None and vectors >= data_size: + break + + # Also check imci_index_stats for vector_rows as secondary indicator + self.cursor.execute( + "SELECT VECTOR_ROWS FROM information_schema.imci_index_stats WHERE SCHEMA_NAME=%s AND TABLE_NAME=%s", + (db_name, self.table_name), + ) + idx_result = self.cursor.fetchone() + if idx_result and data_size is not None and int(idx_result[0]) >= data_size: + break + + time.sleep(2) + + total_time = max(0.0, time.time() - start_time - analyze_elapsed) + log.info(f"PolarDB vector index build completed in {total_time:.1f}s") + + @staticmethod + def vector_to_hex(v: list[float]) -> bytes: + return np.array(v, "float32").tobytes() + + def _insert_batch(self, embeddings: list[list[float]], metadata: list[int], offset: int, size: int) -> None: + """Insert a batch of embeddings using a dedicated connection.""" + conn, cursor = self._create_connection() + try: + db_name = self.db_config["database"] + insert_sql = f"INSERT INTO {db_name}.{self.table_name} (id, v) VALUES (%s, _binary %s)" + batch_data = [] + for i in range(offset, offset + size): + batch_data.append((int(metadata[i]), self.vector_to_hex(embeddings[i]))) + + cursor.executemany(insert_sql, batch_data) + finally: + cursor.close() + conn.close() + + def insert_embeddings( + self, + embeddings: list[list[float]], + metadata: list[int], + **kwargs, + ) -> tuple[int, Exception]: + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + + try: + workers = self.case_config.insert_workers + total = len(embeddings) + batch_size = max(1, total // workers) + + with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor: + futures = [] + for i in range(0, total, batch_size): + offset = i + size = min(batch_size, total - i) + future = executor.submit(self._insert_batch, embeddings, metadata, offset, size) + futures.append(future) + + done, pending = concurrent.futures.wait(futures, return_when=concurrent.futures.FIRST_EXCEPTION) + for future in done: + future.result() + for future in pending: + future.cancel() + + return len(metadata), None + except Exception as e: + log.warning(f"Failed to insert data into table ({self.table_name}), error: {e}") + return 0, e + + def search_embedding( + self, + query: list[float], + k: int = 100, + filters: dict | None = None, + timeout: int | None = None, + **kwargs, + ) -> list[int]: + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + + try: + if filters: + self.cursor.execute( + self.select_sql_with_filter, + (filters.get("id"), self.vector_to_hex(query), k), + ) + else: + self.cursor.execute(self.select_sql, (self.vector_to_hex(query), k)) + return [row[0] for row in self.cursor.fetchall()] + except Exception: + log.exception("Failed to execute search query") + raise diff --git a/vectordb_bench/backend/clients/qdrant_cloud/config.py b/vectordb_bench/backend/clients/qdrant_cloud/config.py index 45373754f..c4466dc17 100644 --- a/vectordb_bench/backend/clients/qdrant_cloud/config.py +++ b/vectordb_bench/backend/clients/qdrant_cloud/config.py @@ -1,6 +1,6 @@ -from typing import Any, TypeVar +from typing import ClassVar, TypeVar -from pydantic import BaseModel, SecretStr, model_validator +from pydantic import BaseModel, SecretStr from ..api import DBCaseConfig, DBConfig, MetricType @@ -10,6 +10,8 @@ # Allowing `api_key` to be left empty, to ensure compatibility with the open-source Qdrant. class QdrantConfig(DBConfig): + _extra_empty_skip: ClassVar[frozenset[str]] = frozenset({"api_key"}) + url: SecretStr api_key: SecretStr | None = None @@ -25,19 +27,6 @@ def to_dict(self) -> dict: "url": self.url.get_secret_value(), } - @model_validator(mode="before") - @classmethod - def not_empty_field(cls, data: Any) -> Any: - if isinstance(data, dict): - skip = set(cls.common_short_configs()) | set(cls.common_long_configs()) | {"api_key"} - for name, v in data.items(): - if name in skip: - continue - if isinstance(v, str) and len(v) == 0: - msg = f"Empty string for field '{name}'!" - raise ValueError(msg) - return data - class QdrantIndexConfig(BaseModel, DBCaseConfig): metric_type: MetricType | None = None diff --git a/vectordb_bench/backend/clients/qdrant_local/cli.py b/vectordb_bench/backend/clients/qdrant_local/cli.py index 7995b99b3..ee3309dc2 100644 --- a/vectordb_bench/backend/clients/qdrant_local/cli.py +++ b/vectordb_bench/backend/clients/qdrant_local/cli.py @@ -49,7 +49,10 @@ def QdrantLocal(**parameters: Unpack[QdrantLocalTypedDict]): run( db=DBTYPE, - db_config=QdrantLocalConfig(url=SecretStr(parameters["url"])), + db_config=QdrantLocalConfig( + db_label=parameters["db_label"], + url=SecretStr(parameters["url"]), + ), db_case_config=QdrantLocalIndexConfig( on_disk=parameters["on_disk"], m=parameters["m"], diff --git a/vectordb_bench/backend/clients/qdrant_local/qdrant_local.py b/vectordb_bench/backend/clients/qdrant_local/qdrant_local.py index 15c790c61..e8e253ac2 100644 --- a/vectordb_bench/backend/clients/qdrant_local/qdrant_local.py +++ b/vectordb_bench/backend/clients/qdrant_local/qdrant_local.py @@ -19,6 +19,9 @@ VectorParams, ) +from vectordb_bench.backend.filter import Filter as BenchFilter +from vectordb_bench.backend.filter import FilterOp + from ..api import VectorDB from .config import QdrantLocalIndexConfig @@ -40,6 +43,11 @@ def qdrant_collection_exists(client: QdrantClient, collection_name: str) -> bool class QdrantLocal(VectorDB): + supported_filter_types: list[FilterOp] = [ + FilterOp.NonFilter, + FilterOp.NumGE, + ] + def __init__( self, dim: int, @@ -60,6 +68,7 @@ def __init__( self._primary_field = "pk" self._vector_field = "vector" + self._query_filter: Filter | None = None client = QdrantClient(**self.db_config) @@ -209,25 +218,29 @@ def search_embedding( """ assert self.client is not None - f = None - if filters: - f = Filter( - must=[ - FieldCondition( - key=self._primary_field, - range=Range( - gt=filters.get("id"), - ), - ), - ], - ) res = self.client.query_points( collection_name=self.collection_name, query=query, limit=k, - query_filter=f, + query_filter=self._query_filter, search_params=SearchParams(**self.search_parameter), timeout=timeout, ).points return [result.id for result in res] + + def prepare_filter(self, filters: BenchFilter): + if filters.type == FilterOp.NonFilter: + self._query_filter = None + elif filters.type == FilterOp.NumGE: + self._query_filter = Filter( + must=[ + FieldCondition( + key=self._primary_field, + range=Range(gte=filters.int_value), + ) + ] + ) + else: + msg = f"Unsupported QdrantLocal filter: {filters}" + raise ValueError(msg) diff --git a/vectordb_bench/backend/clients/seekdb/cli.py b/vectordb_bench/backend/clients/seekdb/cli.py new file mode 100644 index 000000000..9c135220f --- /dev/null +++ b/vectordb_bench/backend/clients/seekdb/cli.py @@ -0,0 +1,60 @@ +import os +from typing import Annotated, Unpack + +import click +from pydantic import SecretStr + +from vectordb_bench.backend.clients import DB +from vectordb_bench.cli.cli import ( + CommonTypedDict, + HNSWFlavor3, + cli, + click_parameter_decorators_from_typed_dict, + run, +) + + +class SeekDBTypedDict(CommonTypedDict): + host: Annotated[str, click.option("--host", type=str, help="SeekDB host", required=True)] + user: Annotated[str, click.option("--user", type=str, help="SeekDB username", required=True)] + password: Annotated[ + str, + click.option( + "--password", + type=str, + help="SeekDB password", + default=lambda: os.environ.get("SEEKDB_PASSWORD", ""), + ), + ] + database: Annotated[str, click.option("--database", type=str, help="Database name", required=True)] + port: Annotated[int, click.option("--port", type=int, help="SeekDB port", default=3306, show_default=True)] + + +class SeekDBHNSWTypedDict(SeekDBTypedDict, HNSWFlavor3): ... + + +@cli.command() +@click_parameter_decorators_from_typed_dict(SeekDBHNSWTypedDict) +def SeekDBHNSW(**parameters: Unpack[SeekDBHNSWTypedDict]): + """Run VectorDBBench against SeekDB with an HNSW index.""" + from ..api import IndexType + from .config import SeekDBConfig, SeekDBHNSWConfig + + run( + DB.SeekDB, + SeekDBConfig( + db_label=parameters["db_label"], + user=SecretStr(parameters["user"]), + password=SecretStr(parameters["password"]), + host=parameters["host"], + port=parameters["port"], + database=parameters["database"], + ), + SeekDBHNSWConfig( + m=parameters["m"], + ef_construction=parameters["ef_construction"], + ef_search=parameters["ef_search"], + index=IndexType.HNSW, + ), + **parameters, + ) diff --git a/vectordb_bench/backend/clients/seekdb/config.py b/vectordb_bench/backend/clients/seekdb/config.py new file mode 100644 index 000000000..01a43ce4b --- /dev/null +++ b/vectordb_bench/backend/clients/seekdb/config.py @@ -0,0 +1,77 @@ +from typing import TypedDict + +from pydantic import BaseModel, SecretStr + +from ..api import DBCaseConfig, DBConfig, IndexType, MetricType + + +class SeekDBConfigDict(TypedDict): + user: str + host: str + port: int + password: str + database: str + + +class SeekDBConfig(DBConfig): + user: SecretStr = SecretStr("root") + password: SecretStr + host: str + port: int = 3306 + database: str + + def to_dict(self) -> SeekDBConfigDict: + return { + "user": self.user.get_secret_value(), + "host": self.host, + "port": self.port, + "password": self.password.get_secret_value(), + "database": self.database, + } + + +class SeekDBIndexConfig(BaseModel): + index: IndexType + metric_type: MetricType | None = None + + def parse_metric(self) -> str: + if self.metric_type == MetricType.L2: + return "l2" + if self.metric_type == MetricType.IP: + return "inner_product" + return "cosine" + + def parse_metric_func_str(self) -> str: + if self.metric_type == MetricType.L2: + return "l2_distance" + if self.metric_type == MetricType.IP: + return "negative_inner_product" + return "cosine_distance" + + +class SeekDBHNSWConfig(SeekDBIndexConfig, DBCaseConfig): + m: int + ef_construction: int + ef_search: int + index: IndexType = IndexType.HNSW + + def index_param(self) -> dict: + return { + "metric_type": self.parse_metric(), + "index_type": self.index.value, + "params": { + "m": self.m, + "ef_construction": self.ef_construction, + }, + } + + def search_param(self) -> dict: + return { + "metric_type": self.parse_metric_func_str(), + "params": {"ef_search": self.ef_search}, + } + + +_seekdb_case_config = { + IndexType.HNSW: SeekDBHNSWConfig, +} diff --git a/vectordb_bench/backend/clients/seekdb/seekdb.py b/vectordb_bench/backend/clients/seekdb/seekdb.py new file mode 100644 index 000000000..f4551a577 --- /dev/null +++ b/vectordb_bench/backend/clients/seekdb/seekdb.py @@ -0,0 +1,282 @@ +import logging +import re +import struct +from collections.abc import Generator +from contextlib import contextmanager +from typing import Any + +import mysql.connector as mysql + +from vectordb_bench.backend.filter import Filter, FilterOp + +from ..api import IndexType, VectorDB +from .config import SeekDBConfigDict, SeekDBHNSWConfig + +log = logging.getLogger(__name__) + +SEEKDB_DEFAULT_LOAD_BATCH_SIZE = 256 + +# Minimum SeekDB version for dbms_index_manager.refresh() after bulk load (see VERSION()). +_SEEKDB_REFRESH_MIN_VERSION = (1, 3, 0) +_SEEKDB_VERSION_IN_VERSION_STRING = re.compile(r"seekdb-v(\d+(?:\.\d+)*)", re.IGNORECASE) + + +def _seekdb_version_tuple(version_row: str | None) -> tuple[int, ...] | None: + if not version_row: + return None + m = _SEEKDB_VERSION_IN_VERSION_STRING.search(version_row.strip()) + if not m: + return None + return tuple(int(p) for p in m.group(1).split(".") if p.isdigit()) + + +def _version_tuple_ge(parsed: tuple[int, ...], minimum: tuple[int, ...]) -> bool: + n = max(len(parsed), len(minimum)) + for i in range(n): + p = parsed[i] if i < len(parsed) else 0 + m = minimum[i] if i < len(minimum) else 0 + if p != m: + return p > m + return True + + +class SeekDB(VectorDB): + supported_filter_types: list[FilterOp] = [ + FilterOp.NonFilter, + FilterOp.NumGE, + ] + # mysql.connector is not thread-safe; ConcurrentInsertRunner uses max_workers=1 when False. + # Streaming fixed-rate inserts use rate_runner with per-thread deepcopy+init() instead. + thread_safe: bool = False + + def __init__( + self, + dim: int, + db_config: SeekDBConfigDict, + db_case_config: SeekDBHNSWConfig, + collection_name: str = "items", + drop_old: bool = False, + **kwargs, + ): + self.name = "SeekDB" + self.dim = dim + self.db_config = db_config + self.db_case_config = db_case_config + self.table_name = collection_name + self.load_batch_size = SEEKDB_DEFAULT_LOAD_BATCH_SIZE + self._index_name = "vidx" + self._primary_field = "id" + self._vector_field = "embedding" + self.expr = "" + + log.info( + f"{self.name} initialized with config:\nDatabase: {self.db_config}\nCase Config: {self.db_case_config}" + ) + + self._conn = None + self._cursor = None + + try: + self._connect() + self._apply_system_settings() + if drop_old: + self._drop_table() + self._create_table() + self._create_index() + finally: + self._disconnect() + + def _connect(self): + try: + self._conn = mysql.connect( + host=self.db_config["host"], + user=self.db_config["user"], + port=self.db_config["port"], + password=self.db_config["password"], + database=self.db_config["database"], + ) + self._cursor = self._conn.cursor() + except mysql.Error: + log.exception("Failed to connect to SeekDB") + raise + + def _disconnect(self): + if self._cursor: + self._cursor.close() + self._cursor = None + if self._conn: + self._conn.close() + self._conn = None + + def _apply_system_settings(self): + if not self._cursor: + raise ValueError("Cursor is not initialized") + + self._cursor.execute('ALTER SYSTEM SET memory_limit = "0M"') + self._cursor.execute("ALTER SYSTEM SET cpu_count = 0") + + def _init_session_settings(self): + if not self._cursor: + raise ValueError("Cursor is not initialized") + + self._cursor.execute("SET autocommit=1") + if self.db_case_config.index == IndexType.HNSW: + ef_search = self.db_case_config.search_param()["params"]["ef_search"] + # SeekDB uses OceanBase-style session vars (not plain hnsw_ef_search). + self._cursor.execute(f"SET ob_hnsw_ef_search={ef_search}") + + @contextmanager + def init(self) -> Generator[None, None, None]: + try: + self._connect() + self._init_session_settings() + yield + finally: + self._disconnect() + + def _drop_table(self): + if not self._cursor: + raise ValueError("Cursor is not initialized") + log.info(f"Dropping table {self.table_name}") + self._cursor.execute(f"DROP TABLE IF EXISTS {self.table_name}") + + def _create_table(self): + """Create a heap table with a vector column. + + ORGANIZATION HEAP specifies a heap-organized table (no clustered primary + key order), which is required by SeekDB for vector workloads. + """ + if not self._cursor: + raise ValueError("Cursor is not initialized") + + log.info(f"Creating heap table {self.table_name}") + create_table_query = f""" + CREATE TABLE {self.table_name} ( + id INT, + embedding VECTOR({self.dim}) + ) ORGANIZATION HEAP; + """ + self._cursor.execute(create_table_query) + + def _create_index(self): + """Create the HNSW vector index immediately after table creation. + + Following Milvus's approach: the index is built upfront so that + streaming inserts are indexed incrementally and searches can run + concurrently with writes (StreamingPerformanceCase). + """ + if not self._cursor: + raise ValueError("Cursor is not initialized") + + index_params = self.db_case_config.index_param() + params = index_params["params"] + index_args = ", ".join(f"{k}={v}" for k, v in params.items()) + + index_query = ( + f"CREATE VECTOR INDEX {self._index_name} " + f"ON {self.table_name}({self._vector_field}) " + f"WITH (distance={index_params['metric_type']}, " + f"type={index_params['index_type']}, {index_args})" + ) + + log.info("Creating HNSW index: %s", index_query) + try: + self._cursor.execute(index_query) + log.info("HNSW index created successfully") + except mysql.Error: + log.exception("Failed to create HNSW index") + raise + + def optimize(self, data_size: int | None = None): + """Post-load hook: refresh index metadata on SeekDB >= 1.3.0 when available. + + Older releases rely on incremental HNSW indexing only. From 1.3.0 onward, + ``CALL dbms_index_manager.refresh()`` aligns on-disk index state after bulk + load (VERSION() strings look like ``... seekdb-v1.3.0.0``). + """ + if not self._cursor: + raise ValueError("Cursor is not initialized") + + self._cursor.execute("SELECT VERSION()") + row = self._cursor.fetchone() + version_str = row[0] if row else None + parsed = _seekdb_version_tuple(version_str) + + if parsed is None or not _version_tuple_ge(parsed, _SEEKDB_REFRESH_MIN_VERSION): + log.info( + "%s optimize: skip dbms_index_manager.refresh (version=%r parsed=%s; need >= %s)", + self.name, + version_str, + parsed, + ".".join(map(str, _SEEKDB_REFRESH_MIN_VERSION)), + ) + return + + log.info( + "%s optimize: SeekDB %s >= %s, calling dbms_index_manager.refresh()", + self.name, + ".".join(map(str, parsed)), + ".".join(map(str, _SEEKDB_REFRESH_MIN_VERSION)), + ) + try: + self._cursor.execute("CALL dbms_index_manager.refresh()") + except mysql.Error: + log.exception("dbms_index_manager.refresh() failed") + raise + + def insert_embeddings( + self, + embeddings: list[list[float]], + metadata: list[int], + **kwargs: Any, + ) -> tuple[int, Exception | None]: + if not self._cursor: + raise ValueError("Cursor is not initialized") + + insert_count = 0 + try: + for batch_start in range(0, len(embeddings), self.load_batch_size): + batch_end = min(batch_start + self.load_batch_size, len(embeddings)) + batch = [(metadata[i], embeddings[i]) for i in range(batch_start, batch_end)] + values = ", ".join(f"({item_id}, '[{','.join(map(str, embedding))}]')" for item_id, embedding in batch) + self._cursor.execute(f"INSERT INTO {self.table_name} VALUES {values}") + insert_count += len(batch) + except mysql.Error: + log.exception("Failed to insert embeddings") + raise + + return insert_count, None + + def prepare_filter(self, filters: Filter): + if filters.type == FilterOp.NonFilter: + self.expr = "" + elif filters.type == FilterOp.NumGE: + self.expr = f"WHERE id >= {filters.int_value}" + else: + msg = f"Unsupported filter for SeekDB: {filters}" + raise ValueError(msg) + + def search_embedding( + self, + query: list[float], + k: int = 100, + ) -> list[int]: + if not self._cursor: + raise ValueError("Cursor is not initialized") + + packed = struct.pack(f"<{len(query)}f", *query) + hex_vec = packed.hex() + + query_str = ( + f"SELECT id FROM {self.table_name} " + f"{self.expr} ORDER BY " + f"{self.db_case_config.parse_metric_func_str()}({self._vector_field}, X'{hex_vec}') " + f"APPROXIMATE LIMIT {k}" + ) + + try: + self._cursor.execute(query_str) + return [row[0] for row in self._cursor.fetchall()] + except mysql.Error: + log.exception("Failed to execute search query") + raise diff --git a/vectordb_bench/backend/clients/tidb/config.py b/vectordb_bench/backend/clients/tidb/config.py index 72abb9111..5e2f032d4 100644 --- a/vectordb_bench/backend/clients/tidb/config.py +++ b/vectordb_bench/backend/clients/tidb/config.py @@ -1,6 +1,6 @@ -from typing import Any, TypedDict +from typing import ClassVar, TypedDict -from pydantic import BaseModel, SecretStr, model_validator +from pydantic import BaseModel, SecretStr from ..api import DBCaseConfig, DBConfig, MetricType @@ -16,6 +16,8 @@ class TiDBConfigDict(TypedDict): class TiDBConfig(DBConfig): + _extra_empty_skip: ClassVar[frozenset[str]] = frozenset({"password"}) + user_name: str = "root" password: SecretStr host: str = "127.0.0.1" @@ -35,19 +37,6 @@ def to_dict(self) -> TiDBConfigDict: "ssl_verify_identity": self.ssl, } - @model_validator(mode="before") - @classmethod - def not_empty_field(cls, data: Any) -> Any: - if isinstance(data, dict): - skip = {"password", "db_label"} - for name, v in data.items(): - if name in skip: - continue - if isinstance(v, str) and len(v) == 0: - msg = f"Empty string for field '{name}'!" - raise ValueError(msg) - return data - class TiDBIndexConfig(BaseModel, DBCaseConfig): metric_type: MetricType | None = None diff --git a/vectordb_bench/backend/clients/tidb/tidb.py b/vectordb_bench/backend/clients/tidb/tidb.py index a5c99bbe4..fba60d41c 100644 --- a/vectordb_bench/backend/clients/tidb/tidb.py +++ b/vectordb_bench/backend/clients/tidb/tidb.py @@ -119,7 +119,7 @@ def _optimize_check_tiflash_replica_progress(self): cursor.execute(f""" SELECT PROGRESS FROM information_schema.tiflash_replica WHERE TABLE_SCHEMA = "{database}" AND TABLE_NAME = "{self.table_name}" - """) # noqa: S608 + """) result = cursor.fetchone() return result[0] except Exception as e: @@ -131,7 +131,7 @@ def _optimize_wait_tiflash_catch_up(self): with self._get_connection() as (conn, cursor): cursor.execute('SET @@TIDB_ISOLATION_READ_ENGINES="tidb,tiflash"') conn.commit() - cursor.execute(f"SELECT COUNT(*) FROM {self.table_name}") # noqa: S608 + cursor.execute(f"SELECT COUNT(*) FROM {self.table_name}") result = cursor.fetchone() return result[0] except Exception as e: @@ -155,7 +155,7 @@ def _optimize_get_tiflash_index_pending_rows(self): SELECT SUM(ROWS_STABLE_NOT_INDEXED) FROM information_schema.tiflash_indexes WHERE TIDB_DATABASE = "{database}" AND TIDB_TABLE = "{self.table_name}" - """) # noqa: S608 + """) result = cursor.fetchone() return result[0] except Exception as e: @@ -172,7 +172,7 @@ def _insert_embeddings_serial( try: with self._get_connection() as (conn, cursor): buf = io.StringIO() - buf.write(f"INSERT INTO {self.table_name} (id, embedding) VALUES ") # noqa: S608 + buf.write(f"INSERT INTO {self.table_name} (id, embedding) VALUES ") for i in range(offset, offset + size): if i > offset: buf.write(",") @@ -220,6 +220,6 @@ def search_embedding( self.cursor.execute(f""" SELECT id FROM {self.table_name} ORDER BY {self.search_fn}(embedding, "{query!s}") LIMIT {k}; - """) # noqa: S608 + """) result = self.cursor.fetchall() return [int(i[0]) for i in result] diff --git a/vectordb_bench/backend/clients/turbopuffer/cli.py b/vectordb_bench/backend/clients/turbopuffer/cli.py index 6fd91f2a8..def442d69 100644 --- a/vectordb_bench/backend/clients/turbopuffer/cli.py +++ b/vectordb_bench/backend/clients/turbopuffer/cli.py @@ -10,33 +10,134 @@ run, ) from .. import DB +from ..api import MetricType +from .config import TurboPufferMultitenantWarmupPolicy + +DEFAULT_PIN_TIMEOUT = 45 * 60 + +ApiKeyOption = Annotated[ + str, + click.option("--api-key", type=str, help="TurboPuffer API key", required=True), +] +RegionOption = Annotated[ + str, + click.option( + "--region", + type=str, + help="TurboPuffer region (e.g. aws-us-east-1, gcp-us-central1)", + required=True, + ), +] +ApiBaseUrlOption = Annotated[ + str, + click.option( + "--api-base-url", + type=str, + help="Override the region-based API URL", + required=False, + default="", + show_default=False, + ), +] +NamespaceOption = Annotated[ + str, + click.option( + "--namespace", + type=str, + help="TurboPuffer namespace", + required=False, + default="vdbbench_test", + show_default=True, + ), +] +PinTimeoutOption = Annotated[ + int, + click.option( + "--pin-timeout", + type=click.IntRange(min=1), + default=DEFAULT_PIN_TIMEOUT, + show_default=True, + help="Seconds to wait for TurboPuffer namespace pinning or unpinning to complete", + ), +] class TurboPufferTypedDict(TypedDict): - api_key: Annotated[ + api_key: ApiKeyOption + region: RegionOption + api_base_url: ApiBaseUrlOption + namespace: NamespaceOption + multitenant_namespace_prefix: Annotated[ str, - click.option("--api-key", type=str, help="TurboPuffer API key", required=True), + click.option( + "--multitenant-namespace-prefix", + type=str, + help="Namespace prefix for CloudMultiTenantSearchCase tenant namespaces", + required=False, + default="vdbbench_mt_", + show_default=True, + ), ] - api_base_url: Annotated[ + scalar_payload_label_field: Annotated[ str, click.option( - "--api-base-url", + "--scalar-payload-label-field", type=str, - help="TurboPuffer API base URL", + help="TurboPuffer attribute used for scalar_label payload and label filtering", required=False, - default="https://api.turbopuffer.com", + default="label", show_default=True, ), ] - namespace: Annotated[ + metric_type: Annotated[ str, click.option( - "--namespace", - type=str, - help="TurboPuffer namespace", + "--metric-type", + type=click.Choice([MetricType.COSINE.value, MetricType.L2.value]), + help="TurboPuffer distance metric type", required=False, - default="vdbbench_test", + default=MetricType.COSINE.value, + show_default=True, + ), + ] + disable_backpressure: Annotated[ + bool, + click.option( + "--disable-backpressure/--enable-backpressure", + type=bool, + default=False, + show_default=True, + help="Disable Turbopuffer write backpressure", + ), + ] + pin_namespace: Annotated[ + bool, + click.option( + "--pin-namespace/--no-pin-namespace", + default=False, + show_default=True, + help="Pin TurboPuffer namespace(s) before benchmark workers run", + ), + ] + pin_replicas: Annotated[ + int, + click.option( + "--pin-replicas", + type=click.IntRange(min=1), + default=1, + show_default=True, + help="Number of TurboPuffer pinning replicas to request", + ), + ] + pin_timeout: PinTimeoutOption + multitenant_warmup_policy: Annotated[ + str, + click.option( + "--multitenant-warmup-policy", + type=click.Choice([policy.value for policy in TurboPufferMultitenantWarmupPolicy]), + default=TurboPufferMultitenantWarmupPolicy.NONE.value, show_default=True, + help="TurboPuffer cache warmup policy for CloudMultiTenantSearchCase tenant namespaces", ), ] @@ -44,19 +145,111 @@ class TurboPufferTypedDict(TypedDict): class TurboPufferIndexTypedDict(CommonTypedDict, TurboPufferTypedDict): ... +class TurboPufferUnpinTypedDict(TypedDict): + """Options for explicit TurboPuffer namespace pinning cleanup. + + Namespace pinning is persistent service state: enabling it before a benchmark + reserves replicas for the namespace until it is cleared. Unpinning is kept + as a separate command so failed or interrupted benchmark runs can be cleaned + up later, and so a billing-affecting teardown action is not hidden behind + ordinary benchmark flags. + """ + + api_key: ApiKeyOption + region: RegionOption + api_base_url: ApiBaseUrlOption + namespace: NamespaceOption + pin_timeout: PinTimeoutOption + + +def target_namespaces_for_pinning(parameters: TurboPufferIndexTypedDict) -> list[str]: + if parameters.get("case_type") != "CloudMultiTenantSearchCase": + return [parameters["namespace"]] + + namespace_prefix = parameters["multitenant_namespace_prefix"] + tenant_prefix = parameters["tenant_prefix"] + tenant_id_width = parameters["tenant_id_width"] + return [ + f"{namespace_prefix}{tenant_prefix}{tenant_id:0{tenant_id_width}d}" + for tenant_id in range(parameters["tenant_count"]) + ] + + +def pin_namespaces_once(parameters: TurboPufferIndexTypedDict) -> None: + from .turbopuffer import namespace_metadata_request, wait_for_namespace_pinning + + for namespace in target_namespaces_for_pinning(parameters): + namespace_metadata_request( + parameters["api_key"], + parameters["region"], + namespace, + "PATCH", + {"pinning": {"replicas": parameters["pin_replicas"]}}, + parameters["api_base_url"] or None, + ) + wait_for_namespace_pinning( + parameters["api_key"], + parameters["region"], + namespace, + parameters["pin_replicas"], + parameters["api_base_url"] or None, + parameters["pin_timeout"], + ) + + @cli.command() @click_parameter_decorators_from_typed_dict(TurboPufferIndexTypedDict) def TurboPuffer(**parameters: Unpack[TurboPufferIndexTypedDict]): from .config import TurboPufferConfig, TurboPufferIndexConfig + pin_target_namespace_count = len(target_namespaces_for_pinning(parameters)) if parameters["pin_namespace"] else 0 + if parameters["pin_namespace"] and not parameters["dry_run"]: + pin_namespaces_once(parameters) + run( db=DB.TurboPuffer, db_config=TurboPufferConfig( db_label=parameters["db_label"], api_key=SecretStr(parameters["api_key"]), - api_base_url=parameters["api_base_url"], + region=parameters["region"], + api_base_url=parameters["api_base_url"] or None, namespace=parameters["namespace"], + multitenant_namespace_prefix=parameters["multitenant_namespace_prefix"], + scalar_payload_label_field=parameters["scalar_payload_label_field"], + pin_namespace=False, + pin_namespace_requested=parameters["pin_namespace"], + pin_replicas=parameters["pin_replicas"], + pin_timeout=parameters["pin_timeout"], + pin_target_namespace_count=pin_target_namespace_count, + ), + db_case_config=TurboPufferIndexConfig( + metric_type=MetricType(parameters["metric_type"]), + disable_backpressure=parameters["disable_backpressure"], + multitenant_warmup_policy=TurboPufferMultitenantWarmupPolicy(parameters["multitenant_warmup_policy"]), ), - db_case_config=TurboPufferIndexConfig(), **parameters, ) + + +@cli.command() +@click_parameter_decorators_from_typed_dict(TurboPufferUnpinTypedDict) +def TurboPufferUnpin(**parameters: Unpack[TurboPufferUnpinTypedDict]): + from .turbopuffer import namespace_metadata_request, wait_for_namespace_pinning + + namespace_metadata_request( + parameters["api_key"], + parameters["region"], + parameters["namespace"], + "PATCH", + {"pinning": None}, + parameters["api_base_url"] or None, + ) + meta = wait_for_namespace_pinning( + parameters["api_key"], + parameters["region"], + parameters["namespace"], + None, + parameters["api_base_url"] or None, + parameters["pin_timeout"], + ) + click.echo(f"TurboPuffer namespace unpinned: {parameters['namespace']} pinning={meta.get('pinning')}") diff --git a/vectordb_bench/backend/clients/turbopuffer/config.py b/vectordb_bench/backend/clients/turbopuffer/config.py index c552299e3..cdb7e8328 100644 --- a/vectordb_bench/backend/clients/turbopuffer/config.py +++ b/vectordb_bench/backend/clients/turbopuffer/config.py @@ -1,18 +1,41 @@ +from enum import StrEnum + from pydantic import BaseModel, SecretStr from ..api import DBCaseConfig, DBConfig, MetricType +class TurboPufferMultitenantWarmupPolicy(StrEnum): + NONE = "none" + ALL = "all" + + class TurboPufferConfig(DBConfig): api_key: SecretStr - api_base_url: str + region: str + api_base_url: str | None = None namespace: str = "vdbbench_test" + multitenant_namespace_prefix: str = "vdbbench_mt_" + scalar_payload_label_field: str = "label" + pin_namespace: bool = False + pin_namespace_requested: bool = False + pin_replicas: int = 1 + pin_timeout: int = 45 * 60 + pin_target_namespace_count: int = 0 def to_dict(self) -> dict: return { "api_key": self.api_key.get_secret_value(), + "region": self.region, "api_base_url": self.api_base_url, "namespace": self.namespace, + "multitenant_namespace_prefix": self.multitenant_namespace_prefix, + "scalar_payload_label_field": self.scalar_payload_label_field, + "pin_namespace": self.pin_namespace, + "pin_namespace_requested": self.pin_namespace_requested, + "pin_replicas": self.pin_replicas, + "pin_timeout": self.pin_timeout, + "pin_target_namespace_count": self.pin_target_namespace_count, } @@ -20,6 +43,8 @@ class TurboPufferIndexConfig(BaseModel, DBCaseConfig): metric_type: MetricType | None = None use_multi_ns_for_filter: bool = False time_wait_warmup: int = 60 * 1 # 1min + disable_backpressure: bool = False + multitenant_warmup_policy: TurboPufferMultitenantWarmupPolicy = TurboPufferMultitenantWarmupPolicy.NONE def parse_metric(self) -> str: if self.metric_type == MetricType.COSINE: @@ -35,3 +60,15 @@ def index_param(self) -> dict: def search_param(self) -> dict: return {} + + +class TurboPufferFtsConfig(BaseModel, DBCaseConfig): + metric_type: MetricType = MetricType.BM25 + time_wait_warmup: int = 60 + disable_backpressure: bool = False + + def index_param(self) -> dict: + return {} + + def search_param(self) -> dict: + return {} diff --git a/vectordb_bench/backend/clients/turbopuffer/turbopuffer.py b/vectordb_bench/backend/clients/turbopuffer/turbopuffer.py index 241551792..918112ede 100644 --- a/vectordb_bench/backend/clients/turbopuffer/turbopuffer.py +++ b/vectordb_bench/backend/clients/turbopuffer/turbopuffer.py @@ -1,17 +1,85 @@ -"""Wrapper around the Pinecone vector database over VectorDB""" +"""Wrapper around the TurboPuffer vector database over VectorDB""" import logging +import os import time from contextlib import contextmanager +from json import dumps, loads +from typing import Any +from urllib.error import HTTPError +from urllib.parse import quote +from urllib.request import Request, urlopen import turbopuffer as tpuf -from vectordb_bench.backend.clients.turbopuffer.config import TurboPufferIndexConfig +from vectordb_bench.backend.clients.turbopuffer.config import ( + TurboPufferFtsConfig, + TurboPufferIndexConfig, + TurboPufferMultitenantWarmupPolicy, +) from vectordb_bench.backend.filter import Filter, FilterOp +from vectordb_bench.backend.payload import PayloadProfile -from ..api import VectorDB +from ..api import PartialInsertError, VectorDB log = logging.getLogger(__name__) +TURBOPUFFER_SEARCHABLE_UNINDEXED_BYTES = 2 * 1024 * 1024 * 1024 +PINNING_POLL_INTERVAL = 10 +PINNING_TIMEOUT = 45 * 60 + + +def namespace_metadata_request( + api_key: str, + region: str, + namespace: str, + method: str, + payload: dict[str, Any] | None = None, + api_base_url: str | None = None, +) -> dict: + base_url = api_base_url or f"https://{region}.turbopuffer.com" + url = f"{base_url.rstrip('/')}/v1/namespaces/{quote(namespace, safe='')}/metadata" + req = Request( # noqa: S310 + url, + data=dumps(payload).encode() if payload is not None else None, + method=method, + headers={ + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + }, + ) + try: + with urlopen(req, timeout=60) as resp: # noqa: S310 + return loads(resp.read().decode() or "{}") + except HTTPError as e: + detail = e.read().decode(errors="replace") + msg = f"Failed to update TurboPuffer namespace metadata: {e.code} {detail}" + raise RuntimeError(msg) from e + + +def wait_for_namespace_pinning( + api_key: str, + region: str, + namespace: str, + replicas: int | None, + api_base_url: str | None = None, + timeout: int = PINNING_TIMEOUT, +) -> dict: + deadline = time.monotonic() + timeout + while True: + meta = namespace_metadata_request(api_key, region, namespace, "GET", api_base_url=api_base_url) + pinning = meta.get("pinning") + if replicas is None: + if pinning is None: + return meta + else: + status = pinning.get("status", {}) if isinstance(pinning, dict) else {} + if pinning and pinning.get("replicas") == replicas and status.get("ready_replicas") == replicas: + return meta + if time.monotonic() >= deadline: + msg = f"Timed out waiting for TurboPuffer pinning state on namespace {namespace}" + raise TimeoutError(msg) + log.info("Waiting for TurboPuffer pinning state on %s: %s", namespace, pinning) + time.sleep(PINNING_POLL_INTERVAL) class TurboPuffer(VectorDB): @@ -25,98 +93,398 @@ def __init__( self, dim: int, db_config: dict, - db_case_config: TurboPufferIndexConfig, + db_case_config: TurboPufferIndexConfig | TurboPufferFtsConfig, drop_old: bool = False, with_scalar_labels: bool = False, **kwargs, ): - """Initialize wrapper around the milvus vector database.""" self.api_key = db_config.get("api_key", "") - self.api_base_url = db_config.get("api_base_url", "") + self.region = db_config.get("region", "") + self.api_base_url = db_config.get("api_base_url") self.namespace = db_config.get("namespace", "") + self.multitenant_namespace_prefix = db_config.get("multitenant_namespace_prefix", "vdbbench_mt_") + self.multitenant_tenant_labels: list[str] = kwargs.get("multitenant_tenant_labels", []) + self._multitenant_touched_tenants: set[str] = set() + self._ns_cache = {} + self.pin_namespace = db_config.get("pin_namespace", False) + self.pin_replicas = db_config.get("pin_replicas", 1) + self.pin_timeout = db_config.get("pin_timeout", PINNING_TIMEOUT) + self._pinning_applied = False self.db_case_config = db_case_config - self.metric = db_case_config.parse_metric() + self._is_fts = isinstance(db_case_config, TurboPufferFtsConfig) + self.metric = None if self._is_fts else db_case_config.parse_metric() self._vector_field = "vector" self._scalar_id_field = "id" self._scalar_label_field = "label" + self._scalar_payload_label_field = db_config.get("scalar_payload_label_field", self._scalar_label_field) + self._text_field = "text" + self._filter_id_field = "filter_id" self.with_scalar_labels = with_scalar_labels - - # Initialize client with new SDK pattern - self.client = tpuf.Turbopuffer(api_key=self.api_key, base_url=self.api_base_url) + self.expr = None if drop_old: - log.info(f"Drop old. delete the namespace: {self.namespace}") - ns = self.client.namespace(self.namespace) - try: - ns.delete_all() - except Exception as e: - log.warning(f"Failed to delete all. Error: {e}") + tmp_client = self._create_client() + if self.multitenant_tenant_labels: + for tenant in self.multitenant_tenant_labels: + try: + tmp_client.namespace(self._namespace_name_for_tenant(tenant)).delete_all() + except Exception as e: + log.warning(f"Failed to delete multitenant namespace {tenant}. Error: {e}") + else: + log.info(f"Drop old. delete the namespace: {self.namespace}") + ns = tmp_client.namespace(self.namespace) + try: + ns.delete_all() + except Exception as e: + log.warning(f"Failed to delete all. Error: {e}") + tmp_client = None + + def _create_client(self) -> tpuf.Turbopuffer: + client_kwargs = {"api_key": self.api_key, "region": self.region} + max_retries = os.getenv("TURBOPUFFER_MAX_RETRIES") + if max_retries is not None: + client_kwargs["max_retries"] = int(max_retries) + if self.api_base_url: + client_kwargs["base_url"] = self.api_base_url + return tpuf.Turbopuffer(**client_kwargs) + + def _apply_namespace_pinning(self): + if not self.pin_namespace or self._pinning_applied: + return + for namespace in self._target_namespaces_for_pinning(): + namespace_metadata_request( + self.api_key, + self.region, + namespace, + "PATCH", + {"pinning": {"replicas": self.pin_replicas}}, + self.api_base_url, + ) + meta = wait_for_namespace_pinning( + self.api_key, + self.region, + namespace, + self.pin_replicas, + self.api_base_url, + self.pin_timeout, + ) + pinning = meta.get("pinning", {}) + status = pinning.get("status", {}) if isinstance(pinning, dict) else {} + log.info( + "TurboPuffer pinning requested for %s: replicas=%s ready_replicas=%s", + namespace, + pinning.get("replicas", self.pin_replicas) if isinstance(pinning, dict) else self.pin_replicas, + status.get("ready_replicas"), + ) + self._pinning_applied = True + + def _target_namespaces_for_pinning(self) -> list[str]: + if self.multitenant_tenant_labels: + return [self._namespace_name_for_tenant(tenant) for tenant in self.multitenant_tenant_labels] + return [self.namespace] + + def __getstate__(self): + state = self.__dict__.copy() + state.pop("client", None) + state.pop("ns", None) + state.pop("_ns_cache", None) + return state + + def __setstate__(self, state: dict): + self.__dict__.update(state) + self.client = self._create_client() + self._ns_cache = {} + self.ns = None + + @classmethod + def supports_full_text_search(cls) -> bool: + return True + + def has_text_field(self) -> bool: + return bool(getattr(self, "_is_fts", False) and getattr(self, "_text_field", None)) @contextmanager def init(self): + self.client = self._create_client() + self._ns_cache = {} self.ns = self.client.namespace(self.namespace) + self._apply_namespace_pinning() yield + def supports_multitenant(self) -> bool: + return True + + def set_multitenant_context(self, tenant_labels: list[str]) -> None: + self.multitenant_tenant_labels = tenant_labels + + def _namespace_name_for_tenant(self, tenant: str | None) -> str: + if tenant is None: + return self.namespace + return f"{self.multitenant_namespace_prefix}{tenant}" + + def _namespace_for_tenant(self, tenant: str | None): + name = self._namespace_name_for_tenant(tenant) + ns = self._ns_cache.get(name) + if ns is None: + ns = self.client.namespace(name) + self._ns_cache[name] = ns + return ns + def optimize(self, data_size: int | None = None): # turbopuffer responds to the request # once the cache warming operation has been started. # It does not wait for the operation to complete, # which can take multiple minutes for large namespaces. - self.ns.hint_cache_warm() + warmed_namespaces = self._warmup_target_namespaces() + for namespace in warmed_namespaces: + self._namespace_for_tenant(namespace).hint_cache_warm() + if not warmed_namespaces: + log.info("TurboPuffer cache warmup skipped") + return log.info(f"warming up but no api waiting for complete. just sleep {self.db_case_config.time_wait_warmup}s") time.sleep(self.db_case_config.time_wait_warmup) + def _warmup_target_namespaces(self) -> list[str | None]: + if not self.multitenant_tenant_labels: + return [None] + policy = getattr( + self.db_case_config, + "multitenant_warmup_policy", + TurboPufferMultitenantWarmupPolicy.NONE, + ) + if policy == TurboPufferMultitenantWarmupPolicy.ALL: + return self.multitenant_tenant_labels + return [] + def insert_embeddings( self, embeddings: list[list[float]], metadata: list[int], labels_data: list[str] | None = None, + tenant_labels_data: list[str] | None = None, **kwargs, ) -> tuple[int, Exception]: + vectors = [embedding.tolist() if hasattr(embedding, "tolist") else embedding for embedding in embeddings] + if tenant_labels_data is not None: + inserted = 0 + successful_tenants: dict[str, int] = {} + for tenant in sorted(set(tenant_labels_data)): + self._multitenant_touched_tenants.add(tenant) + idxs = [i for i, label in enumerate(tenant_labels_data) if label == tenant] + try: + upsert_columns = { + self._scalar_id_field: [metadata[i] for i in idxs], + self._vector_field: [vectors[i] for i in idxs], + } + if self.with_scalar_labels: + upsert_columns[self._scalar_payload_label_field] = [labels_data[i] for i in idxs] + self._namespace_for_tenant(tenant).write( + upsert_columns=upsert_columns, + distance_metric=self.metric, + disable_backpressure=self.db_case_config.disable_backpressure, + ) + except Exception as e: + msg = ( + "TurboPuffer multitenant insert failed for " + f"tenant={tenant} after writing {inserted} rows; " + f"successful_tenants={successful_tenants}; " + f"failed_tenant_count={len(idxs)}" + ) + err = PartialInsertError( + msg, + inserted_count=inserted, + successful_tenants=successful_tenants, + failed_tenant=tenant, + failed_tenant_count=len(idxs), + cause=e, + ) + log.warning(f"Failed to insert. Error: {err}") + return inserted, err + inserted += len(idxs) + successful_tenants[tenant] = len(idxs) + return inserted, None try: if self.with_scalar_labels: self.ns.write( - columns={ + upsert_columns={ self._scalar_id_field: metadata, - self._vector_field: embeddings, - self._scalar_label_field: labels_data, + self._vector_field: vectors, + self._scalar_payload_label_field: labels_data, }, distance_metric=self.metric, + disable_backpressure=self.db_case_config.disable_backpressure, ) else: self.ns.write( - columns={ + upsert_columns={ self._scalar_id_field: metadata, - self._vector_field: embeddings, + self._vector_field: vectors, }, distance_metric=self.metric, + disable_backpressure=self.db_case_config.disable_backpressure, ) except Exception as e: log.warning(f"Failed to insert. Error: {e}") + return 0, e return len(embeddings), None + def supports_payload_profile(self, payload_profile: PayloadProfile) -> bool: + return payload_profile in { + PayloadProfile.IDS_ONLY, + PayloadProfile.SCALAR_LABEL, + PayloadProfile.VECTOR, + } + + def poll_insert_readiness(self, expected_count: int) -> dict: + if getattr(self, "multitenant_tenant_labels", []): + unindexed_by_tenant = {} + tenant_labels = ( + sorted(getattr(self, "_multitenant_touched_tenants", set())) or self.multitenant_tenant_labels + ) + for tenant in tenant_labels: + metadata = self._namespace_for_tenant(tenant).metadata() + if not isinstance(metadata, dict): + metadata = metadata.model_dump() if hasattr(metadata, "model_dump") else vars(metadata) + index = metadata.get("index", {}) + if not isinstance(index, dict): + index = index.model_dump() if hasattr(index, "model_dump") else vars(index) + unindexed_by_tenant[tenant] = metadata.get("unindexed_bytes", index.get("unindexed_bytes", 0)) + max_unindexed_bytes = max(unindexed_by_tenant.values(), default=0) + return { + "fully_searchable": max_unindexed_bytes <= TURBOPUFFER_SEARCHABLE_UNINDEXED_BYTES, + "fully_indexed": max_unindexed_bytes == 0, + "additional_parameters": { + "disable_backpressure": self.db_case_config.disable_backpressure, + "max_unindexed_bytes": max_unindexed_bytes, + }, + } + metadata = self.ns.metadata() + if not isinstance(metadata, dict): + metadata = metadata.model_dump() if hasattr(metadata, "model_dump") else vars(metadata) + index = metadata.get("index", {}) + if not isinstance(index, dict): + index = index.model_dump() if hasattr(index, "model_dump") else vars(index) + unindexed_bytes = metadata.get("unindexed_bytes", index.get("unindexed_bytes", 0)) + return { + "fully_searchable": unindexed_bytes <= TURBOPUFFER_SEARCHABLE_UNINDEXED_BYTES, + "fully_indexed": unindexed_bytes == 0, + "additional_parameters": {"disable_backpressure": self.db_case_config.disable_backpressure}, + } + + def insert_documents( + self, + texts: list[str], + doc_ids: list[str], + **kwargs, + ) -> tuple[int, Exception | None]: + if not getattr(self, "_is_fts", False): + msg = "TurboPuffer full-text insert requires TurboPufferFtsConfig" + raise RuntimeError(msg) + assert self.ns is not None, "should self.init() first" + + docs = list(texts) + if len(docs) != len(doc_ids): + msg = f"Mismatch between texts ({len(docs)}) and doc_ids ({len(doc_ids)}) lengths" + raise ValueError(msg) + filter_ids = kwargs.get("filter_ids") + if filter_ids is not None and len(filter_ids) != len(docs): + msg = f"Mismatch between texts ({len(docs)}) and filter_ids ({len(filter_ids)}) lengths" + raise ValueError(msg) + + text_field = self._text_field + upsert_columns = { + self._scalar_id_field: [str(doc_id) for doc_id in doc_ids], + text_field: docs, + } + if filter_ids is not None: + upsert_columns[self._filter_id_field] = [int(filter_id) for filter_id in filter_ids] + schema = { + text_field: { + "type": "string", + "full_text_search": True, + } + } + if filter_ids is not None: + schema[self._filter_id_field] = { + "type": "int", + "filterable": True, + } + try: + self.ns.write( + upsert_columns=upsert_columns, + schema=schema, + disable_backpressure=self.db_case_config.disable_backpressure, + ) + except Exception as e: + log.warning(f"Failed to insert FTS docs. Error: {e}") + return 0, e + return len(docs), None + def search_embedding( self, query: list[float], k: int = 100, timeout: int | None = None, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + tenant: str | None = None, ) -> list[int]: - res = self.ns.query( - rank_by=("vector", "ANN", query), - top_k=k, - filters=self.expr, - ) - return [row.id for row in res.rows] if res.rows is not None else [] + query_kwargs = { + "rank_by": ("vector", "ANN", query), + "top_k": k, + "filters": self.expr, + } + if payload_profile == PayloadProfile.VECTOR: + query_kwargs["include_attributes"] = [self._vector_field] + elif payload_profile == PayloadProfile.SCALAR_LABEL: + query_kwargs["include_attributes"] = [self._scalar_payload_label_field] + res = self._namespace_for_tenant(tenant).query(**query_kwargs) + return [int(row.id) for row in res.rows] if res.rows is not None else [] + + def search_documents( + self, + query: str, + k: int = 100, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + **kwargs, + ) -> list[str]: + if not getattr(self, "_is_fts", False): + msg = "TurboPuffer full-text search requires TurboPufferFtsConfig" + raise RuntimeError(msg) + if not self.supports_document_payload_profile(payload_profile): + msg = f"TurboPuffer does not support document payload_profile={payload_profile.value}" + raise NotImplementedError(msg) + assert self.ns is not None, "should self.init() first" + + query_kwargs = { + "rank_by": (self._text_field, "BM25", query), + "top_k": k, + } + if self.expr is not None: + query_kwargs["filters"] = self.expr + if payload_profile == PayloadProfile.TEXT: + query_kwargs["include_attributes"] = [self._text_field] + res = self.ns.query(**query_kwargs) + rows = getattr(res, "rows", None) or [] + return [str(row.id) for row in rows if getattr(row, "id", None) is not None] def prepare_filter(self, filters: Filter): if filters.type == FilterOp.NonFilter: self.expr = None elif filters.type == FilterOp.NumGE: - self.expr = (self._scalar_id_field, "Gte", filters.int_value) + if self._is_fts: + if getattr(filters, "int_field", None) != self._filter_id_field: + msg = f"TurboPuffer FTS filters only support int_field='{self._filter_id_field}'" + raise ValueError(msg) + self.expr = (self._filter_id_field, "Gte", filters.int_value) + else: + self.expr = (self._scalar_id_field, "Gte", filters.int_value) elif filters.type == FilterOp.StrEqual: - self.expr = (self._scalar_label_field, "Eq", filters.label_value) + if self._is_fts: + msg = f"Not support Filter for TurboPuffer FTS - {filters}" + raise ValueError(msg) + self.expr = (self._scalar_payload_label_field, "Eq", filters.label_value) else: msg = f"Not support Filter for TurboPuffer - {filters}" raise ValueError(msg) diff --git a/vectordb_bench/backend/clients/vectorchord/__init__.py b/vectordb_bench/backend/clients/vectorchord/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vectordb_bench/backend/clients/vectorchord/cli.py b/vectordb_bench/backend/clients/vectorchord/cli.py new file mode 100644 index 000000000..2b3e3862b --- /dev/null +++ b/vectordb_bench/backend/clients/vectorchord/cli.py @@ -0,0 +1,267 @@ +import os +from typing import Annotated, Unpack + +import click +from pydantic import SecretStr + +from vectordb_bench.backend.clients import DB + +from ....cli.cli import ( + CommonTypedDict, + cli, + click_parameter_decorators_from_typed_dict, + run, +) + + +class VectorChordTypedDict(CommonTypedDict): + user_name: Annotated[ + str, + click.option("--user-name", type=str, help="Db username", required=True), + ] + password: Annotated[ + str, + click.option( + "--password", + type=str, + help="Postgres database password", + default=lambda: os.environ.get("POSTGRES_PASSWORD", ""), + show_default="$POSTGRES_PASSWORD", + ), + ] + + host: Annotated[str, click.option("--host", type=str, help="Db host", required=True)] + port: Annotated[ + int, + click.option( + "--port", + type=int, + help="Postgres database port", + default=5432, + show_default=True, + required=False, + ), + ] + db_name: Annotated[str, click.option("--db-name", type=str, help="Db name", required=True)] + max_parallel_workers: Annotated[ + int | None, + click.option( + "--max-parallel-workers", + type=int, + help="Sets the maximum number of parallel workers for index creation", + required=False, + ), + ] + quantization_type: Annotated[ + str | None, + click.option( + "--quantization-type", + type=click.Choice(["vector", "halfvec", "rabitq8", "rabitq4"]), + help="Quantization type for vectors", + default="vector", + show_default=True, + ), + ] + + +class VectorChordRQTypedDict(VectorChordTypedDict): + lists: Annotated[ + int | None, + click.option( + "--lists", + type=int, + help="Number of IVF lists for vchordrq index", + ), + ] + probes: Annotated[ + int | None, + click.option( + "--probes", + type=int, + help="Number of probes during search", + default=10, + show_default=True, + ), + ] + epsilon: Annotated[ + float | None, + click.option( + "--epsilon", + type=float, + help="Reranking precision factor (0.0-4.0, higher is more accurate but slower)", + default=1.9, + show_default=True, + ), + ] + residual_quantization: Annotated[ + bool, + click.option( + "--residual-quantization/--no-residual-quantization", + type=bool, + help="Enable residual quantization for improved accuracy", + default=False, + show_default=True, + ), + ] + rerank_in_table: Annotated[ + bool, + click.option( + "--rerank-in-table/--no-rerank-in-table", + type=bool, + help="Read vectors from table instead of storing in index (saves storage, degrades query performance)", + default=False, + show_default=True, + ), + ] + spherical_centroids: Annotated[ + bool, + click.option( + "--spherical-centroids/--no-spherical-centroids", + type=bool, + help="L2-normalize centroids during K-means (recommended for cosine/IP)", + default=False, + show_default=True, + ), + ] + build_threads: Annotated[ + int | None, + click.option( + "--build-threads", + type=int, + help="Number of threads for index building (range: 1-255)", + ), + ] + degree_of_parallelism: Annotated[ + int | None, + click.option( + "--degree-of-parallelism", + type=int, + help="Degree of parallelism for index build (range: 1-256, default: 32)", + ), + ] + max_scan_tuples: Annotated[ + int | None, + click.option( + "--max-scan-tuples", + type=int, + help="Max tuples to scan before stopping (-1 for unlimited)", + ), + ] + + +@cli.command() +@click_parameter_decorators_from_typed_dict(VectorChordRQTypedDict) +def VectorChordRQ( + **parameters: Unpack[VectorChordRQTypedDict], +): + from .config import VectorChordConfig, VectorChordRQConfig + + run( + db=DB.VectorChord, + db_config=VectorChordConfig( + db_label=parameters["db_label"], + user_name=SecretStr(parameters["user_name"]), + password=SecretStr(parameters["password"]), + host=parameters["host"], + port=parameters["port"], + db_name=parameters["db_name"], + ), + db_case_config=VectorChordRQConfig( + quantization_type=parameters["quantization_type"], + lists=parameters["lists"], + probes=parameters["probes"], + epsilon=parameters["epsilon"], + residual_quantization=parameters["residual_quantization"], + rerank_in_table=parameters["rerank_in_table"], + spherical_centroids=parameters["spherical_centroids"], + build_threads=parameters["build_threads"], + degree_of_parallelism=parameters["degree_of_parallelism"], + max_scan_tuples=parameters["max_scan_tuples"], + max_parallel_workers=parameters["max_parallel_workers"], + ), + **parameters, + ) + + +class VectorChordGraphTypedDict(VectorChordTypedDict): + m: Annotated[ + int | None, + click.option( + "--m", + type=int, + help="Max neighbors per vertex (default: 32)", + ), + ] + ef_construction: Annotated[ + int | None, + click.option( + "--ef-construction", + type=int, + help="Dynamic list size during insertion (default: 64)", + ), + ] + bits: Annotated[ + int | None, + click.option( + "--bits", + type=int, + help="RaBitQ quantization ratio (1 or 2, default: 2)", + ), + ] + ef_search: Annotated[ + int | None, + click.option( + "--ef-search", + type=int, + help="Dynamic list size for search (default: 64)", + default=64, + show_default=True, + ), + ] + beam_search: Annotated[ + int | None, + click.option( + "--beam-search", + type=int, + help="Batch vertex access width during search (default: 1)", + ), + ] + max_scan_tuples: Annotated[ + int | None, + click.option( + "--max-scan-tuples", + type=int, + help="Max tuples to scan before stopping (-1 for unlimited)", + ), + ] + + +@cli.command() +@click_parameter_decorators_from_typed_dict(VectorChordGraphTypedDict) +def VectorChordGraph( + **parameters: Unpack[VectorChordGraphTypedDict], +): + from .config import VectorChordConfig, VectorChordGraphConfig + + run( + db=DB.VectorChord, + db_config=VectorChordConfig( + db_label=parameters["db_label"], + user_name=SecretStr(parameters["user_name"]), + password=SecretStr(parameters["password"]), + host=parameters["host"], + port=parameters["port"], + db_name=parameters["db_name"], + ), + db_case_config=VectorChordGraphConfig( + quantization_type=parameters["quantization_type"], + m=parameters["m"], + ef_construction=parameters["ef_construction"], + bits=parameters["bits"], + ef_search=parameters["ef_search"], + beam_search=parameters["beam_search"], + max_parallel_workers=parameters["max_parallel_workers"], + max_scan_tuples=parameters["max_scan_tuples"], + ), + **parameters, + ) diff --git a/vectordb_bench/backend/clients/vectorchord/config.py b/vectordb_bench/backend/clients/vectorchord/config.py new file mode 100644 index 000000000..95916eb06 --- /dev/null +++ b/vectordb_bench/backend/clients/vectorchord/config.py @@ -0,0 +1,196 @@ +from abc import abstractmethod +from typing import Literal, LiteralString, TypedDict + +from pydantic import BaseModel, SecretStr + +from ..api import DBCaseConfig, DBConfig, IndexType, MetricType + + +class VectorChordConfigDict(TypedDict): + """These keys will be directly used as kwargs in psycopg connection string, + so the names must match exactly psycopg API""" + + user: str + password: str + host: str + port: int + dbname: str + + +class VectorChordConfig(DBConfig): + user_name: SecretStr = SecretStr("postgres") + password: SecretStr + host: str = "localhost" + port: int = 5432 + db_name: str = "vectordb" + + def to_dict(self) -> VectorChordConfigDict: + user_str = self.user_name.get_secret_value() + pwd_str = self.password.get_secret_value() + return { + "host": self.host, + "port": self.port, + "dbname": self.db_name, + "user": user_str, + "password": pwd_str, + } + + +_METRIC_OPS = { + "vector": { + MetricType.L2: "vector_l2_ops", + MetricType.IP: "vector_ip_ops", + MetricType.COSINE: "vector_cosine_ops", + }, + "halfvec": { + MetricType.L2: "halfvec_l2_ops", + MetricType.IP: "halfvec_ip_ops", + MetricType.COSINE: "halfvec_cosine_ops", + }, + "rabitq8": { + MetricType.L2: "rabitq8_l2_ops", + MetricType.IP: "rabitq8_ip_ops", + MetricType.COSINE: "rabitq8_cosine_ops", + }, + "rabitq4": { + MetricType.L2: "rabitq4_l2_ops", + MetricType.IP: "rabitq4_ip_ops", + MetricType.COSINE: "rabitq4_cosine_ops", + }, +} + + +class VectorChordIndexConfig(BaseModel, DBCaseConfig): + metric_type: MetricType | None = None + create_index_before_load: bool = False + create_index_after_load: bool = True + quantization_type: Literal["vector", "halfvec", "rabitq8", "rabitq4"] = "vector" + + def parse_metric(self) -> str: + ops = _METRIC_OPS.get(self.quantization_type, _METRIC_OPS["vector"]) + return ops.get(self.metric_type, ops[MetricType.COSINE]) + + def parse_metric_fun_op(self) -> LiteralString: + if self.metric_type == MetricType.L2: + return "<->" + if self.metric_type == MetricType.IP: + return "<#>" + return "<=>" + + @abstractmethod + def index_param(self) -> dict: ... + + @abstractmethod + def search_param(self) -> dict: ... + + @abstractmethod + def session_param(self) -> dict: ... + + +class VectorChordRQConfig(VectorChordIndexConfig): + index: IndexType = IndexType.VCHORDRQ + # Build parameters (top-level options) + residual_quantization: bool = False + rerank_in_table: bool = False + degree_of_parallelism: int | None = None # default 32, range [1, 256] + # Build parameters ([build.internal] section) + lists: int | None = None + spherical_centroids: bool = False + build_threads: int | None = None # range [1, 255] + # PostgreSQL tuning parameter + max_parallel_workers: int | None = None # sets max_parallel_workers & max_parallel_maintenance_workers + # Search parameters (GUCs) + probes: int | None = 10 + epsilon: float | None = 1.9 # range [0.0, 4.0] + max_scan_tuples: int | None = None # default -1, range [-1, 2147483647] + + def index_param(self) -> dict: + options_parts = [] + if self.rerank_in_table: + options_parts.append("rerank_in_table = true") + if self.residual_quantization: + options_parts.append("residual_quantization = true") + if self.degree_of_parallelism is not None: + options_parts.append(f"degree_of_parallelism = {self.degree_of_parallelism}") + options_parts.append("[build.internal]") + if self.lists is not None: + options_parts.append(f"lists = [{self.lists}]") + if self.spherical_centroids: + options_parts.append("spherical_centroids = true") + if self.build_threads is not None: + options_parts.append(f"build_threads = {self.build_threads}") + + return { + "metric": self.parse_metric(), + "index_type": self.index.value, + "quantization_type": self.quantization_type, + "options": "\n".join(options_parts), + "max_parallel_workers": self.max_parallel_workers, + } + + def search_param(self) -> dict: + return { + "metric_fun_op": self.parse_metric_fun_op(), + } + + def session_param(self) -> dict: + params = {} + if self.probes is not None: + params["vchordrq.probes"] = str(self.probes) + if self.epsilon is not None: + params["vchordrq.epsilon"] = str(self.epsilon) + if self.max_scan_tuples is not None: + params["vchordrq.max_scan_tuples"] = str(self.max_scan_tuples) + return params + + +class VectorChordGraphConfig(VectorChordIndexConfig): + index: IndexType = IndexType.VCHORDG + # Build parameters + m: int | None = None # default 32, max neighbors per vertex + ef_construction: int | None = None # default 64 + bits: int | None = None # default 2, quantization ratio (1 or 2) + # PostgreSQL tuning parameter + max_parallel_workers: int | None = None + # Search parameters (GUCs) + ef_search: int | None = 64 # range [1, 65535] + beam_search: int | None = None # default 1 + max_scan_tuples: int | None = None # default -1, range [-1, 2147483647] + + def index_param(self) -> dict: + options_parts = [] + if self.m is not None: + options_parts.append(f"m = {self.m}") + if self.ef_construction is not None: + options_parts.append(f"ef_construction = {self.ef_construction}") + if self.bits is not None: + options_parts.append(f"bits = {self.bits}") + + return { + "metric": self.parse_metric(), + "index_type": self.index.value, + "quantization_type": self.quantization_type, + "options": "\n".join(options_parts), + "max_parallel_workers": self.max_parallel_workers, + } + + def search_param(self) -> dict: + return { + "metric_fun_op": self.parse_metric_fun_op(), + } + + def session_param(self) -> dict: + params = {} + if self.ef_search is not None: + params["vchordg.ef_search"] = str(self.ef_search) + if self.beam_search is not None: + params["vchordg.beam_search"] = str(self.beam_search) + if self.max_scan_tuples is not None: + params["vchordg.max_scan_tuples"] = str(self.max_scan_tuples) + return params + + +_vectorchord_case_config = { + IndexType.VCHORDRQ: VectorChordRQConfig, + IndexType.VCHORDG: VectorChordGraphConfig, +} diff --git a/vectordb_bench/backend/clients/vectorchord/vectorchord.py b/vectordb_bench/backend/clients/vectorchord/vectorchord.py new file mode 100644 index 000000000..77a462e75 --- /dev/null +++ b/vectordb_bench/backend/clients/vectorchord/vectorchord.py @@ -0,0 +1,325 @@ +"""Wrapper around the VectorChord vector database over VectorDB""" + +import logging +from collections.abc import Generator +from contextlib import contextmanager +from typing import Any + +import numpy as np +import psycopg +from pgvector.psycopg import register_vector +from psycopg import Connection, Cursor, sql + +from ...filter import Filter, FilterOp +from ..api import VectorDB +from .config import VectorChordConfigDict, VectorChordIndexConfig + +log = logging.getLogger(__name__) + + +class VectorChord(VectorDB): + """Use psycopg instructions""" + + thread_safe: bool = False + supported_filter_types: list[FilterOp] = [ + FilterOp.NonFilter, + FilterOp.NumGE, + ] + + conn: psycopg.Connection[Any] | None = None + cursor: psycopg.Cursor[Any] | None = None + + _search: sql.Composed + where_clause: str = "" + + def __init__( + self, + dim: int, + db_config: VectorChordConfigDict, + db_case_config: VectorChordIndexConfig, + collection_name: str = "vectorchord_collection", + drop_old: bool = False, + **kwargs, + ): + self.name = "VectorChord" + self.db_config = db_config + self.case_config = db_case_config + self.table_name = collection_name + self.dim = dim + + self._index_name = "vectorchord_index" + self._primary_field = "id" + self._vector_field = "embedding" + + index_param = self.case_config.index_param() + self._quantization_type = index_param["quantization_type"] + self._index_method = index_param["index_type"] + + self.conn, self.cursor = self._create_connection(**self.db_config) + + # create vectorchord extension if not exists + self.cursor.execute("CREATE EXTENSION IF NOT EXISTS vchord CASCADE") + self.conn.commit() + + log.info(f"{self.name} config values: {self.db_config}\n{self.case_config}") + if not any( + ( + self.case_config.create_index_before_load, + self.case_config.create_index_after_load, + ), + ): + msg = ( + f"{self.name} config must create an index using create_index_before_load or create_index_after_load" + f"{self.name} config values: {self.db_config}\n{self.case_config}" + ) + log.error(msg) + raise RuntimeError(msg) + + if drop_old: + self._drop_index() + self._drop_table() + self._create_table(dim) + if self.case_config.create_index_before_load: + self._create_index() + + self.cursor.close() + self.conn.close() + self.cursor = None + self.conn = None + + @staticmethod + def _create_connection(**kwargs) -> tuple[Connection, Cursor]: + conn = psycopg.connect(**kwargs) + register_vector(conn) + conn.autocommit = False + cursor = conn.cursor() + + assert conn is not None, "Connection is not initialized" + assert cursor is not None, "Cursor is not initialized" + + return conn, cursor + + @contextmanager + def init(self) -> Generator[None, None, None]: + self.conn, self.cursor = self._create_connection(**self.db_config) + + # index configuration may have commands defined that we should set during each client session + session_options: dict[str, Any] = self.case_config.session_param() + + if len(session_options) > 0: + for setting_name, setting_val in session_options.items(): + command = sql.SQL("SET {setting_name} " + "= {setting_val};").format( + setting_name=sql.Identifier(setting_name), + setting_val=sql.Literal(str(setting_val)), + ) + log.debug(command.as_string(self.cursor)) + self.cursor.execute(command) + self.conn.commit() + + try: + yield + finally: + self.cursor.close() + self.conn.close() + self.cursor = None + self.conn = None + + def _drop_table(self): + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + log.info(f"{self.name} client drop table : {self.table_name}") + + self.cursor.execute( + sql.SQL("DROP TABLE IF EXISTS public.{table_name}").format( + table_name=sql.Identifier(self.table_name), + ), + ) + self.conn.commit() + + def optimize(self, data_size: int | None = None): + self._post_insert() + + def _post_insert(self): + log.info(f"{self.name} post insert before optimize") + if self.case_config.create_index_after_load: + self._drop_index() + self._create_index() + + def _drop_index(self): + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + log.info(f"{self.name} client drop index : {self._index_name}") + + drop_index_sql = sql.SQL("DROP INDEX IF EXISTS {index_name}").format( + index_name=sql.Identifier(self._index_name), + ) + log.debug(drop_index_sql.as_string(self.cursor)) + self.cursor.execute(drop_index_sql) + self.conn.commit() + + def _set_parallel_index_build_param(self): + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + + index_param = self.case_config.index_param() + + if index_param["max_parallel_workers"] is not None: + self.cursor.execute( + sql.SQL("SET max_parallel_workers TO '{}';").format( + index_param["max_parallel_workers"], + ), + ) + self.cursor.execute( + sql.SQL("SET max_parallel_maintenance_workers TO '{}';").format( + index_param["max_parallel_workers"], + ), + ) + self.cursor.execute( + sql.SQL("ALTER TABLE {} SET (parallel_workers = {});").format( + sql.Identifier(self.table_name), + index_param["max_parallel_workers"], + ), + ) + self.conn.commit() + + results = self.cursor.execute(sql.SQL("SHOW max_parallel_workers;")).fetchall() + results.extend(self.cursor.execute(sql.SQL("SHOW max_parallel_maintenance_workers;")).fetchall()) + log.info(f"{self.name} parallel index creation parameters: {results}") + + def _create_index(self): + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + log.info(f"{self.name} client create index : {self._index_name}") + + index_param: dict[str, Any] = self.case_config.index_param() + self._set_parallel_index_build_param() + + index_create_sql = sql.SQL( + """ + CREATE INDEX IF NOT EXISTS {index_name} ON public.{table_name} + USING {index_method} (embedding {embedding_metric}) + """, + ).format( + index_name=sql.Identifier(self._index_name), + table_name=sql.Identifier(self.table_name), + index_method=sql.SQL(self._index_method), + embedding_metric=sql.Identifier(index_param["metric"]), + ) + + options_str = index_param.get("options", "") + if options_str: + with_clause = sql.SQL( + "WITH (options = $vchord$\n{options}\n$vchord$);", + ).format(options=sql.SQL(options_str)) + else: + with_clause = sql.SQL(";") + + full_sql = index_create_sql + sql.SQL(" ") + with_clause + log.debug(full_sql.as_string(self.cursor)) + self.cursor.execute(full_sql) + self.conn.commit() + + def _create_table(self, dim: int): + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + + try: + log.info(f"{self.name} client create table : {self.table_name}") + + col_type = self._quantization_type + if col_type in ("rabitq8", "rabitq4"): + # rabitq types need vector column + quantization during insert + col_type = "vector" + + self.cursor.execute( + sql.SQL( + "CREATE TABLE IF NOT EXISTS public.{table_name} " + "(id BIGINT PRIMARY KEY, embedding {col_type}({dim}));", + ).format( + table_name=sql.Identifier(self.table_name), + col_type=sql.SQL(col_type), + dim=dim, + ), + ) + self.conn.commit() + except Exception as e: + log.warning(f"Failed to create vectorchord table: {self.table_name} error: {e}") + raise e from None + + def insert_embeddings( + self, + embeddings: list[list[float]], + metadata: list[int], + **kwargs: Any, + ) -> tuple[int, Exception | None]: + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + + try: + metadata_arr = np.array(metadata) + embeddings_arr = np.array(embeddings) + + if self._quantization_type == "halfvec": + with self.cursor.copy( + sql.SQL("COPY public.{table_name} FROM STDIN (FORMAT BINARY)").format( + table_name=sql.Identifier(self.table_name), + ), + ) as copy: + copy.set_types(["bigint", "halfvec"]) + for i, row in enumerate(metadata_arr): + copy.write_row((row, np.float16(embeddings_arr[i]))) + else: + # vector, rabitq8, rabitq4 all store as vector column + with self.cursor.copy( + sql.SQL("COPY public.{table_name} FROM STDIN (FORMAT BINARY)").format( + table_name=sql.Identifier(self.table_name), + ), + ) as copy: + copy.set_types(["bigint", "vector"]) + for i, row in enumerate(metadata_arr): + copy.write_row((row, embeddings_arr[i])) + self.conn.commit() + + return len(metadata), None + except Exception as e: + log.warning(f"Failed to insert data into vectorchord table ({self.table_name}), error: {e}") + return 0, e + + def _generate_search_query(self) -> sql.Composed: + # Search query cast type: rabitq8/rabitq4 queries still accept ::vector input + cast_type = "vector" + return sql.Composed( + [ + sql.SQL("SELECT id FROM public.{table_name} {where_clause} ORDER BY embedding ").format( + table_name=sql.Identifier(self.table_name), + where_clause=sql.SQL(self.where_clause), + ), + sql.SQL(self.case_config.search_param()["metric_fun_op"]), + sql.SQL(f" %s::{cast_type} LIMIT %s::int"), + ], + ) + + def prepare_filter(self, filters: Filter): + if filters.type == FilterOp.NonFilter: + self.where_clause = "" + elif filters.type == FilterOp.NumGE: + self.where_clause = f"WHERE {self._primary_field} >= {filters.int_value}" + else: + msg = f"Not support Filter for VectorChord - {filters}" + raise ValueError(msg) + + self._search = self._generate_search_query() + + def search_embedding( + self, + query: list[float], + k: int = 100, + timeout: int | None = None, + **kwargs: Any, + ) -> list[int]: + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + + q = np.asarray(query) + result = self.cursor.execute(self._search, (q, k), prepare=True, binary=True) + return [int(i[0]) for i in result.fetchall()] diff --git a/vectordb_bench/backend/clients/vespa/config.py b/vectordb_bench/backend/clients/vespa/config.py index 3d4a1deaf..5eeced0f6 100644 --- a/vectordb_bench/backend/clients/vespa/config.py +++ b/vectordb_bench/backend/clients/vespa/config.py @@ -49,3 +49,28 @@ def parse_metric(self, metric_type: MetricType) -> VespaMetric: return "hamming" case _: raise NotImplementedError + + +class VespaFtsConfig(BaseModel, DBCaseConfig): + metric_type: MetricType = MetricType.BM25 + bm25_k1: float | None = None + bm25_b: float | None = None + bm25_avgdl: float | None = None + feed_client_command: str = "vespa" + feed_client_connections: int | None = None + + def index_param(self) -> dict: + return {} + + def search_param(self) -> dict: + return {} + + def rank_properties(self) -> list[tuple[str, str]]: + properties = [] + if self.bm25_k1 is not None: + properties.append(("bm25(text).k1", str(self.bm25_k1))) + if self.bm25_b is not None: + properties.append(("bm25(text).b", str(self.bm25_b))) + if self.bm25_avgdl is not None: + properties.append(("bm25(text).averageFieldLength", str(self.bm25_avgdl))) + return properties diff --git a/vectordb_bench/backend/clients/vespa/vespa.py b/vectordb_bench/backend/clients/vespa/vespa.py index 5288bc04c..12fa65d5e 100644 --- a/vectordb_bench/backend/clients/vespa/vespa.py +++ b/vectordb_bench/backend/clients/vespa/vespa.py @@ -1,24 +1,62 @@ import datetime +import json import logging import math +import shutil +import subprocess +import tempfile +import threading +import time from collections.abc import Generator from contextlib import contextmanager +from typing import Any from vespa import application +from vectordb_bench.backend.filter import Filter, FilterOp +from vectordb_bench.backend.payload import PayloadProfile + from ..api import VectorDB from . import util -from .config import VespaHNSWConfig +from .config import VespaFtsConfig, VespaHNSWConfig log = logging.getLogger(__name__) +VESPA_DOC_COUNT_POLL_INTERVAL_SEC = 2 +VESPA_DOC_COUNT_TIMEOUT_SEC = 1800 +VESPA_FEED_OUTPUT_TAIL_CHARS = 4000 + + +def _document_id_from_hit(hit: dict) -> str | None: + fields = hit.get("fields") + if isinstance(fields, dict) and "id" in fields: + return str(fields["id"]) + + document_id = hit.get("id") + if document_id is None: + return None + + document_id = str(document_id) + if document_id.startswith("id:") and "::" in document_id: + return document_id.rsplit("::", 1)[-1] + return document_id + + +def _tail_text(value: str, limit: int = VESPA_FEED_OUTPUT_TAIL_CHARS) -> str: + return value[-limit:] if len(value) > limit else value + class Vespa(VectorDB): + supported_filter_types: list[FilterOp] = [ + FilterOp.NonFilter, + FilterOp.NumGE, + ] + def __init__( self, dim: int, db_config: dict[str, str], - db_case_config: VespaHNSWConfig | None = None, + db_case_config: VespaHNSWConfig | VespaFtsConfig | None = None, collection_name: str = "VectorDBBenchCollection", drop_old: bool = False, **kwargs, @@ -26,7 +64,11 @@ def __init__( self.dim = dim self.db_config = db_config self.case_config = db_case_config or VespaHNSWConfig() + self._is_fts = isinstance(self.case_config, VespaFtsConfig) self.schema_name = collection_name + self._text_field = "text" + self._filter_id_field = "filter_id" + self._filter_expr: str | None = None client = self.deploy_http() client.wait_for_application_up() @@ -35,8 +77,13 @@ def __init__( try: client.delete_all_docs("vectordbbench_content", self.schema_name) except Exception: + if self._is_fts: + raise drop_old = False log.exception(f"Vespa client drop_old schema: {self.schema_name}") + else: + if self._is_fts: + self._wait_for_document_count(client, 0, "drop_old") @contextmanager def init(self) -> Generator[None, None, None]: @@ -54,8 +101,39 @@ def init(self) -> Generator[None, None, None]: >>> self.insert_embeddings() """ self.client = application.Vespa(self.db_config["url"], port=self.db_config["port"]) - yield + self._reset_fts_feed_client() + try: + yield + self._finish_fts_feed_client() + finally: + self._cleanup_fts_feed_client() + self.client = None + + def __getstate__(self): + state = self.__dict__.copy() + # Multiprocessing search uses spawn, so DB instances are pickled before + # workers run. Vespa client and feed-client handles are process-local. + state.pop("client", None) + state.pop("_feed_proc", None) + state.pop("_feed_stdout_file", None) + state.pop("_feed_stderr_file", None) + state.pop("_feed_lock", None) + return state + + def __setstate__(self, state: dict): + self.__dict__.update(state) self.client = None + self._feed_proc = None + self._feed_stdout_file = None + self._feed_stderr_file = None + self._feed_lock = threading.Lock() + + @classmethod + def supports_full_text_search(cls) -> bool: + return True + + def has_text_field(self) -> bool: + return bool(getattr(self, "_is_fts", False) and getattr(self, "_text_field", None)) def need_normalize_cosine(self) -> bool: """Wheather this database need to normalize dataset to support COSINE""" @@ -84,6 +162,180 @@ def insert_embeddings( self.client.feed_iterable(data, self.schema_name) return len(embeddings), None + def insert_documents( + self, + texts: list[str], + doc_ids: list[str], + **kwargs, + ) -> tuple[int, Exception | None]: + if not self._is_fts: + msg = "Vespa full-text insert requires VespaFtsConfig" + raise RuntimeError(msg) + assert self.client is not None + + if len(texts) != len(doc_ids): + msg = f"Mismatch between texts ({len(texts)}) and doc_ids ({len(doc_ids)}) lengths" + raise ValueError(msg) + filter_ids = kwargs.get("filter_ids") + if filter_ids is not None and len(filter_ids) != len(texts): + msg = f"Mismatch between texts ({len(texts)}) and filter_ids ({len(filter_ids)}) lengths" + raise ValueError(msg) + + try: + self._write_fts_feed_batch(texts, doc_ids, filter_ids) + except Exception as exc: + log.warning("Vespa feed failed for schema %s", self.schema_name, exc_info=True) + return 0, exc + + return len(texts), None + + def _reset_fts_feed_client(self) -> None: + self._feed_proc = None + self._feed_stdout_file = None + self._feed_stderr_file = None + self._feed_written_count = 0 + self._feed_lock = threading.Lock() + + def _ensure_fts_feed_client(self) -> subprocess.Popen: + if self._feed_proc is not None: + return self._feed_proc + command = self.case_config.feed_client_command + if shutil.which(command) is None: + msg = ( + f"Vespa feed client command {command!r} was not found. " + "Install the Vespa CLI or set VespaFtsConfig.feed_client_command." + ) + raise RuntimeError(msg) + + connections = self.case_config.feed_client_connections or 8 + cmd = [ + command, + "feed", + "-", + "--target", + self._feed_target(), + "--connections", + str(connections), + "--inflight", + "0", + "--progress", + "0", + ] + log.info("Start Vespa feed client: %s", " ".join(cmd)) + + # These files must stay open until the feed client exits; Popen writes to them after this method returns. + self._feed_stdout_file = tempfile.TemporaryFile(mode="w+", encoding="utf-8") # noqa: SIM115 + self._feed_stderr_file = tempfile.TemporaryFile(mode="w+", encoding="utf-8") # noqa: SIM115 + self._feed_proc = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=self._feed_stdout_file, + stderr=self._feed_stderr_file, + text=True, + ) + return self._feed_proc + + def _write_fts_feed_batch(self, texts: list[str], doc_ids: list[str], filter_ids: list[int] | None = None) -> None: + lines = [] + for i, (doc_id, text) in enumerate(zip(doc_ids, texts, strict=True)): + fields = {"id": str(doc_id), "text": text} + if filter_ids is not None: + fields[self._filter_id_field] = int(filter_ids[i]) + operation = { + "put": self._vespa_document_id(str(doc_id)), + "fields": fields, + } + lines.append(json.dumps(operation, ensure_ascii=False, separators=(",", ":"))) + + with self._feed_lock: + proc = self._ensure_fts_feed_client() + returncode = proc.poll() + if returncode is not None: + msg = f"Vespa feed client exited before all documents were written: returncode={returncode}" + raise RuntimeError(msg) + assert proc.stdin is not None + proc.stdin.write("\n".join(lines)) + proc.stdin.write("\n") + proc.stdin.flush() + self._feed_written_count += len(lines) + + def _finish_fts_feed_client(self) -> None: + if self._feed_proc is None: + return + with self._feed_lock: + proc = self._feed_proc + if proc.stdin is not None and not proc.stdin.closed: + proc.stdin.close() + returncode = proc.wait() + assert self._feed_stdout_file is not None + assert self._feed_stderr_file is not None + self._feed_stdout_file.seek(0) + self._feed_stderr_file.seek(0) + stdout = self._feed_stdout_file.read() + stderr = self._feed_stderr_file.read() + + count = self._feed_written_count + metrics = self._parse_feed_metrics(stdout) + ok_count = int(metrics.get("feeder.ok.count", count)) if metrics else count + error_count = int(metrics.get("feeder.error.count", 0)) if metrics else 0 + response_error_count = int(metrics.get("http.response.error.count", 0)) if metrics else 0 + + if returncode != 0 or error_count or ok_count != count: + msg = ( + "Vespa feed client failed " + f"returncode={returncode}, written={count}, ok={ok_count}, " + f"feed_errors={error_count}, response_errors={response_error_count}, " + f"stdout_tail={_tail_text(stdout)!r}, stderr_tail={_tail_text(stderr)!r}" + ) + raise RuntimeError(msg) + if response_error_count: + log.warning( + "Vespa feed client reported %d HTTP response errors, but all %d documents were accepted", + response_error_count, + ok_count, + ) + + log.info("Vespa feed client inserted %d docs; metrics=%s", ok_count, metrics) + self._feed_proc = None + + def _cleanup_fts_feed_client(self) -> None: + proc = getattr(self, "_feed_proc", None) + if proc is not None and proc.poll() is None: + proc.kill() + proc.wait() + for file_attr in ("_feed_stdout_file", "_feed_stderr_file"): + file_obj = getattr(self, file_attr, None) + if file_obj is not None: + file_obj.close() + setattr(self, file_attr, None) + self._feed_proc = None + + def _vespa_document_id(self, doc_id: str) -> str: + return f"id:{self.schema_name}:{self.schema_name}::{doc_id}" + + def _feed_target(self) -> str: + target = str(self.db_config["url"]).rstrip("/") + port = self.db_config.get("port") + if port is not None and f":{port}" not in target.rsplit("/", 1)[-1]: + target = f"{target}:{port}" + return target + + def _parse_feed_metrics(self, output: str) -> dict[str, Any]: + text = output.strip() + if not text: + return {} + try: + return json.loads(text) + except json.JSONDecodeError: + start = text.rfind("\n{") + if start >= 0: + try: + return json.loads(text[start + 1 :]) + except json.JSONDecodeError: + pass + log.warning("Failed to parse Vespa feed client metrics: %s", _tail_text(output)) + return {} + def search_embedding( self, query: list[float], @@ -107,14 +359,17 @@ def search_embedding( embedding_field = "embedding" if self.case_config.quantization_type == "none" else "embedding_binary" yql = ( - f"select id from {self.schema_name} where " # noqa: S608 + f"select id from {self.schema_name} where " f"{{targetHits: {k}, hnsw.exploreAdditionalHits: {extra_ef}}}" f"nearestNeighbor({embedding_field}, query_embedding)" ) + prepared_filter = getattr(self, "_filter_expr", None) if filters: id_filter = filters.get("id") - yql += f" and id >= {id_filter}" + prepared_filter = f"id >= {id_filter}" + if prepared_filter: + yql += f" and {prepared_filter}" query_embedding = query if self.case_config.quantization_type == "none" else util.binarize_tensor(query) @@ -123,6 +378,61 @@ def search_embedding( result = self.client.query({"yql": yql, "input.query(query_embedding)": query_embedding, "ranking": ranking}) return [child["fields"]["id"] for child in result.get_json()["root"]["children"]] + def search_documents( + self, + query: str, + k: int = 100, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + **kwargs, + ) -> list[str]: + if not self._is_fts: + msg = "Vespa full-text search requires VespaFtsConfig" + raise RuntimeError(msg) + if not self.supports_document_payload_profile(payload_profile): + msg = f"Vespa does not support document payload_profile={payload_profile.value}" + raise NotImplementedError(msg) + assert self.client is not None + + selected_fields = "id" + if payload_profile == PayloadProfile.TEXT: + selected_fields = f"id, {self._text_field}" + yql = f"select {selected_fields} from {self.schema_name} where userQuery()" + if self._filter_expr: + yql += f" and {self._filter_expr}" + result = self.client.query( + { + "yql": yql, + "query": query, + "type": "any", + "ranking": "bm25", + "hits": k, + "default-index": self._text_field, + } + ) + children = result.get_json().get("root", {}).get("children", []) or [] + ids = [] + for child in children: + if not isinstance(child, dict): + continue + doc_id = _document_id_from_hit(child) + if doc_id is not None: + ids.append(doc_id) + return ids + + def prepare_filter(self, filters: Filter) -> None: + if filters.type == FilterOp.NonFilter: + self._filter_expr = None + return + if filters.type == FilterOp.NumGE: + expected_field = self._filter_id_field if self._is_fts else "id" + if getattr(filters, "int_field", None) != expected_field: + msg = f"Vespa filters only support int_field='{expected_field}' in this mode" + raise ValueError(msg) + self._filter_expr = f"{expected_field} >= {filters.int_value}" + return + msg = f"Not support Filter for Vespa - {filters}" + raise ValueError(msg) + def optimize(self, data_size: int | None = None): """optimize will be called between insertion and search in performance cases. @@ -132,6 +442,35 @@ def optimize(self, data_size: int | None = None): Time(insert the dataset) + Time(optimize) will be recorded as "load_duration" metric Optimize's execution time is limited, the limited time is based on cases. """ + if self._is_fts and data_size is not None: + assert self.client is not None + self._wait_for_document_count(self.client, data_size, "post_insert") + + def _document_count(self, client: application.Vespa) -> int: + result = client.query({"yql": f"select id from {self.schema_name} where true", "hits": 0}) + root = result.get_json().get("root", {}) + fields = root.get("fields", {}) + if "totalCount" not in fields: + msg = f"Vespa count query did not return totalCount for schema {self.schema_name}: {root}" + raise RuntimeError(msg) + return int(fields["totalCount"]) + + def _wait_for_document_count(self, client: application.Vespa, expected_count: int, stage: str) -> None: + deadline = time.monotonic() + VESPA_DOC_COUNT_TIMEOUT_SEC + last_count = None + while True: + last_count = self._document_count(client) + if last_count == expected_count: + log.info("Vespa %s document count reached %d", stage, expected_count) + return + if time.monotonic() >= deadline: + msg = ( + f"Timed out waiting for Vespa {stage} document count to reach " + f"{expected_count}; last_count={last_count}" + ) + raise TimeoutError(msg) + log.info("Waiting for Vespa %s document count: current=%d expected=%d", stage, last_count, expected_count) + time.sleep(VESPA_DOC_COUNT_POLL_INTERVAL_SEC) @property def application_package(self): @@ -151,6 +490,39 @@ def _create_application_package(self): ValidationID, ) + tomorrow = datetime.date.today() + datetime.timedelta(days=1) + + if self._is_fts: + fields = [ + Field("id", "string", indexing=["summary", "attribute"]), + Field("filter_id", "int", indexing=["summary", "attribute"]), + Field( + "text", + "string", + indexing=["index", "summary"], + index="enable-bm25", + stemming="none", + ), + ] + return ApplicationPackage( + "vectordbbench", + [ + Schema( + self.schema_name, + Document(fields), + rank_profiles=[ + RankProfile( + name="bm25", + first_phase="bm25(text)", + inherits="default", + rank_properties=self.case_config.rank_properties(), + ), + ], + ) + ], + validations=[Validation(ValidationID.fieldTypeChange, until=tomorrow)], + ) + fields = [ Field( "id", @@ -185,8 +557,6 @@ def _create_application_package(self): ) ) - tomorrow = datetime.date.today() + datetime.timedelta(days=1) - return ApplicationPackage( "vectordbbench", [ diff --git a/vectordb_bench/backend/clients/volc_mysql/cli.py b/vectordb_bench/backend/clients/volc_mysql/cli.py new file mode 100755 index 000000000..9ce563473 --- /dev/null +++ b/vectordb_bench/backend/clients/volc_mysql/cli.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +from typing import Annotated, Unpack + +import click +from pydantic import SecretStr + +from vectordb_bench.backend.clients import DB + +from ....cli.cli import ( + CommonTypedDict, + cli, + click_parameter_decorators_from_typed_dict, + run, +) + + +class VolcMySQLTypedDict(CommonTypedDict): + user_name: Annotated[ + str, + click.option( + "--username", + type=str, + help="Username", + required=True, + ), + ] + password: Annotated[ + str, + click.option( + "--password", + type=str, + help="Password", + required=True, + ), + ] + + host: Annotated[ + str, + click.option( + "--host", + type=str, + help="Db host", + default="127.0.0.1", + ), + ] + + port: Annotated[ + int, + click.option( + "--port", + type=int, + default=3306, + help="DB Port", + ), + ] + + +class VolcMySQLHNSWTypedDict(VolcMySQLTypedDict): + m: Annotated[ + int | None, + click.option( + "--m", + type=int, + help="M parameter in HNSW vector indexing", + required=False, + ), + ] + + ef_search: Annotated[ + int | None, + click.option( + "--ef-search", + type=int, + help="Session variable loose_hnsw_ef_search", + required=False, + ), + ] + + ef_construction: Annotated[ + int | None, + click.option( + "--ef-construction", + type=int, + help="HNSW ef_construction", + required=False, + ), + ] + + quant_algorithm: Annotated[ + str | None, + click.option( + "--quant-algorithm", + type=click.Choice(["NONE", "SQ", "PQ"]), + help="Quantization algorithm", + required=False, + ), + ] + + quant_type: Annotated[ + str | None, + click.option( + "--quant-type", + type=click.Choice(["16_bit", "8_bit", "4_bit", "binary"]), + help="Quantization type", + required=False, + ), + ] + + +@cli.command() +@click_parameter_decorators_from_typed_dict(VolcMySQLHNSWTypedDict) +def VolcMySQLHNSW( + **parameters: Unpack[VolcMySQLHNSWTypedDict], +): + from .config import VolcMySQLConfig, VolcMySQLHNSWConfig + + run( + db=DB.VolcMySQL, + db_config=VolcMySQLConfig( + db_label=parameters["db_label"], + user_name=parameters["username"], + password=SecretStr(parameters["password"]), + host=parameters["host"], + port=parameters["port"], + ), + db_case_config=VolcMySQLHNSWConfig( + M=parameters["m"], + ef_search=parameters["ef_search"], + ef_construction=parameters["ef_construction"], + quant_algorithm=parameters["quant_algorithm"], + quant_type=parameters["quant_type"], + ), + **parameters, + ) diff --git a/vectordb_bench/backend/clients/volc_mysql/config.py b/vectordb_bench/backend/clients/volc_mysql/config.py new file mode 100755 index 000000000..aa4224848 --- /dev/null +++ b/vectordb_bench/backend/clients/volc_mysql/config.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +from typing import TypedDict + +from pydantic import BaseModel, SecretStr + +from ..api import DBCaseConfig, DBConfig, IndexType, MetricType + + +class VolcMySQLConfigDict(TypedDict): + """Keys used directly as kwargs in mysql.connector.connect; + names must match mysql-connector-python's API.""" + + user: str + password: str + host: str + port: int + + +class VolcMySQLConfig(DBConfig): + user_name: str = "root" + password: SecretStr + host: str = "127.0.0.1" + port: int = 3306 + + def to_dict(self) -> VolcMySQLConfigDict: + return { + "host": self.host, + "port": self.port, + "user": self.user_name, + "password": self.password.get_secret_value(), + } + + +class VolcMySQLIndexConfig(BaseModel): + """Base index config for VolcMySQL""" + + metric_type: MetricType | None = None + + def parse_metric(self) -> str: + if self.metric_type == MetricType.L2: + return "l2" + if self.metric_type == MetricType.COSINE: + return "cosine" + msg = f"Metric type {self.metric_type} is not supported!" + raise ValueError(msg) + + +class VolcMySQLHNSWConfig(VolcMySQLIndexConfig, DBCaseConfig): + M: int | None = None + ef_search: int | None = None + ef_construction: int | None = None + quant_algorithm: str | None = None + quant_type: str | None = None + index: IndexType = IndexType.HNSW + + def index_param(self) -> dict: + return { + "metric_type": self.parse_metric(), + "index_type": self.index.value, + "M": self.M, + "ef_construction": self.ef_construction, + "quant_algorithm": self.quant_algorithm, + "quant_type": self.quant_type, + } + + def search_param(self) -> dict: + return { + "metric_type": self.parse_metric(), + "ef_search": self.ef_search, + } + + +_volcmysql_case_config = { + IndexType.HNSW: VolcMySQLHNSWConfig, +} diff --git a/vectordb_bench/backend/clients/volc_mysql/volc_mysql.py b/vectordb_bench/backend/clients/volc_mysql/volc_mysql.py new file mode 100755 index 000000000..455286019 --- /dev/null +++ b/vectordb_bench/backend/clients/volc_mysql/volc_mysql.py @@ -0,0 +1,411 @@ +#!/usr/bin/env python3 +import json +import logging +import os +import struct +import tempfile +from contextlib import contextmanager +from pathlib import Path + +import mysql.connector as mysql +import numpy as np + +from vectordb_bench.backend.filter import Filter, FilterOp + +from ..api import VectorDB +from .config import VolcMySQLConfigDict, VolcMySQLIndexConfig + +log = logging.getLogger(__name__) + + +def _encode_batch_to_tsv( + metadata: list[int], + embeddings: list[list[float]], + dim: int, + tsv_path: str, + *, + binary: bool = True, +) -> None: + """Sort (id, vector) pairs by id ascending and write them to ``tsv_path`` + for bulk ``LOAD DATA`` ingestion. + + ``binary=True`` (default) encodes each vector as hex of little-endian + float32 bytes -> ``\\t\\n``, consumed by + ``... (id, @h) SET v = UNHEX(@h)``. + + ``binary=False`` (to_vector fallback) writes the vector as a JSON-style + ``[f1,f2,...]`` literal -> ``\\t[..]\\n``, consumed by + ``... (id, @v) SET v = to_vector(@v)``. The literal contains no tab or + newline, so it stays delimiter-safe in the TSV transport. + """ + order = np.argsort(metadata) + with Path(tsv_path).open("w", buffering=1 << 20) as f: + if binary: + pack_fmt = f"<{dim}f" + for i in order: + hex_str = struct.pack(pack_fmt, *embeddings[i]).hex() + f.write(f"{metadata[i]}\t{hex_str}\n") + else: + for i in order: + vec_str = "[" + ",".join(repr(float(x)) for x in embeddings[i]) + "]" + f.write(f"{metadata[i]}\t{vec_str}\n") + + +def _build_index_attrs_json(index_param: dict) -> str: + """Build the SECONDARY_ENGINE_ATTRIBUTE JSON payload for CREATE VECTOR INDEX + from a case-config index_param dict. None values are dropped so the server + sees only the attributes the user explicitly set. + """ + attrs = { + "algorithm": "hnsw", + "distance": index_param.get("metric_type"), + "m": index_param.get("M"), + "ef_construction": index_param.get("ef_construction"), + "quant_algorithm": index_param.get("quant_algorithm"), + "quant_type": index_param.get("quant_type"), + } + attrs = {k: v for k, v in attrs.items() if v is not None} + return json.dumps(attrs, ensure_ascii=False, separators=(",", ":")) + + +class VolcMySQL(VectorDB): + # mysql.connector is not thread-safe; ConcurrentInsertRunner uses + # max_workers=1 when False. rate_runner branches on db.name to give + # each worker thread its own connection. + thread_safe: bool = False + + supported_filter_types: list[FilterOp] = [ + FilterOp.NonFilter, + FilterOp.NumGE, + ] + + def __init__( + self, + dim: int, + db_config: VolcMySQLConfigDict, + db_case_config: VolcMySQLIndexConfig, + collection_name: str = "vec_collection", + drop_old: bool = False, + **kwargs, + ): + self.name = "VolcMySQL" + self.db_config = db_config + self.case_config = db_case_config + self.db_name = "vectordbbench" + self.table_name = collection_name + self.dim = dim + + self.conn = None + self.cursor = None + self.admin_cursor = None + + # Active filter predicate selected by prepare_filter(). Runners call + # prepare_filter() (never search_embedding(filters=...)), so the + # filtered WHERE clause is chosen here, not per query. + self._filtered = False + + if drop_old: + self.conn, self.cursor, self.admin_cursor = self._create_connection() + try: + self._drop_db() + self._create_db_table(dim) + finally: + self.cursor.close() + self.admin_cursor.close() + self.conn.close() + self.conn = None + self.cursor = None + self.admin_cursor = None + + def _create_connection(self): + conn = mysql.connect( + host=self.db_config["host"], + user=self.db_config["user"], + port=self.db_config["port"], + password=self.db_config["password"], + allow_local_infile=True, + ) + cursor = conn.cursor() + admin_cursor = conn.cursor() + + assert conn is not None, "Connection is not initialized" + assert cursor is not None, "Cursor is not initialized" + assert admin_cursor is not None, "Admin cursor is not initialized" + + return conn, cursor, admin_cursor + + def _drop_db(self): + assert self.conn is not None, "Connection is not initialized" + assert self.admin_cursor is not None, "Cursor is not initialized" + log.info(f"{self.name} client drop db : {self.db_name}") + + # flush tables before dropping database to avoid some locking issue + self.admin_cursor.execute("FLUSH TABLES") + self.admin_cursor.execute(f"DROP DATABASE IF EXISTS {self.db_name}") + self.admin_cursor.execute("COMMIT") + self.admin_cursor.execute("FLUSH TABLES") + + def _create_db_table(self, dim: int): + assert self.conn is not None, "Connection is not initialized" + assert self.admin_cursor is not None, "Cursor is not initialized" + + try: + log.info(f"{self.name} client create database : {self.db_name}") + self.admin_cursor.execute(f"CREATE DATABASE {self.db_name}") + + log.info(f"{self.name} client create table : {self.table_name}") + self.admin_cursor.execute(f"USE {self.db_name}") + + create_table_sql = f""" + CREATE TABLE {self.table_name} ( + id INT PRIMARY KEY, + v VECTOR({self.dim}) NOT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 + """ + self.admin_cursor.execute(create_table_sql) + self.admin_cursor.execute("COMMIT") + + except Exception as e: + log.warning(f"Failed to create table: {self.table_name} error: {e}") + raise e from None + + def _probe_binary_support(self) -> bool: + """Probe once whether the server accepts the raw little-endian float32 + binary VECTOR path: ``UNHEX()`` insert and the ``_binary`` query + introducer. Returns ``True`` only if both succeed; otherwise ``False`` + so :meth:`init` selects the ``to_vector()`` text path for insert and + search. + + Runs inside a session-local ``TEMPORARY TABLE`` with a throwaway + 4-dim vector, so it never touches the benchmark table and is + independent of the real vector dimension. A probe failure is expected + (not an error) on builds without binary VECTOR support and only flips + the path; it never raises. + """ + probe = [1.0, 2.0, 3.0, 4.0] + blob = struct.pack(f"<{len(probe)}f", *probe) + # Qualify with the (already-existing) benchmark schema: the client never + # issues USE, so the connection has no default database to host the temp table. + tmp = f"`{self.db_name}`._vdbb_binprobe" + cur = self.conn.cursor(buffered=True) + try: + cur.execute(f"CREATE TEMPORARY TABLE {tmp} (id INT PRIMARY KEY, v VECTOR(4))") + cur.execute(f"INSERT INTO {tmp} (id, v) VALUES (1, UNHEX(%s))", (blob.hex(),)) + cur.execute(f"SELECT id FROM {tmp} ORDER BY L2_DISTANCE(v, _binary %s) LIMIT 1", (blob,)) + cur.fetchall() + except mysql.Error as e: + log.warning(f"{self.name}: binary VECTOR path unsupported, falling back to to_vector() text path: {e}") + return False + else: + log.info(f"{self.name}: binary VECTOR path supported; using raw-binary insert + query") + return True + finally: + try: + cur.execute(f"DROP TEMPORARY TABLE IF EXISTS {tmp}") + except mysql.Error: + log.debug("Failed to drop binary-probe temp table", exc_info=True) + cur.close() + + @contextmanager + def init(self): + """create and destory connections to database. + + Examples: + >>> with self.init(): + >>> self.insert_embeddings() + """ + self.conn, self.cursor, self.admin_cursor = self._create_connection() + try: + # The binary probe creates a TEMPORARY TABLE qualified with self.db_name. + # When drop_old=False on a fresh server, the schema may not exist yet -- + # ensure it does so the probe lands on a real database and doesn't + # spuriously fall back to the text path. + self.admin_cursor.execute(f"CREATE DATABASE IF NOT EXISTS {self.db_name}") + + # Load-phase session tuning. SESSION-scoped; resets when the + # connection closes. No GLOBAL or instance-level changes. + try: + self.admin_cursor.execute("SET SESSION unique_checks = 0") + self.admin_cursor.execute("SET SESSION foreign_key_checks = 0") + except mysql.Error as e: + log.warning(f"Could not apply load-phase session tuning: {e}") + + # Per-batch TSV file numbering for LOAD DATA bulk load. + self._batch_counter = 0 + + index_param = self.case_config.index_param() + search_param = self.case_config.search_param() + + if search_param.get("ef_search") is not None: + try: + self.admin_cursor.execute(f"SET loose_hnsw_ef_search = {int(search_param['ef_search'])}") + self.conn.commit() + except mysql.Error: + log.warning( + f"Could not set loose_hnsw_ef_search = {int(search_param['ef_search'])}, " + "using server defaults" + ) + + # prebuild SQL strings + dist_func = "L2_DISTANCE" if index_param["metric_type"] == "l2" else "COSINE_DISTANCE" + # Raw-binary VECTOR path: send float32 vectors as little-endian bytes and let the + # server consume them directly -- UNHEX(@h) on insert, the `_binary` introducer on + # query -- with no to_vector() text parse and no Python str() formatting (+71% c80 + # QPS, recall identical). `_binary ` stays constant-foldable so the HNSW + # index scan is preserved (UNHEX() is NOT, hence hex is used only on the load path). + # + # Not every MySQL-compatible build accepts the binary path, so we AUTO-PROBE it once + # per connection (see _probe_binary_support) and fall back to the to_vector() text + # path -- for BOTH insert and query -- when it is unsupported. VDB_BINARY_VEC overrides + # the probe: "1" forces binary (skip probe), "0" forces the to_vector() text path. + force = os.environ.get("VDB_BINARY_VEC") + if force == "1": + self._binary_vec = True + elif force == "0": + self._binary_vec = False + else: + self._binary_vec = self._probe_binary_support() + vec_expr = "_binary %s" if self._binary_vec else "to_vector(%s)" + # No FORCE INDEX hint: optimize() creates idx_v, but read_write streaming + # cases run search before optimize, so hinting a not-yet-existing index + # would error every pre-optimize search. The optimizer picks idx_v once + # it exists; before that, the seq scan is the only valid plan anyway. + self.select_sql = ( + f"SELECT id FROM {self.db_name}.{self.table_name} ORDER BY {dist_func}(v, {vec_expr}) LIMIT %s" + ) + self.select_sql_with_filter = ( + f"SELECT id FROM {self.db_name}.{self.table_name} WHERE id >= %s ORDER BY " + f"{dist_func}(v, {vec_expr}) LIMIT %s" + ) + + yield + finally: + try: + if self.cursor is not None: + self.cursor.close() + if self.admin_cursor is not None: + self.admin_cursor.close() + if self.conn is not None: + self.conn.close() + finally: + self.cursor = None + self.admin_cursor = None + self.conn = None + + def optimize(self, data_size: int | None = None) -> None: + assert self.conn is not None, "Connection is not initialized" + assert self.admin_cursor is not None, "Admin cursor is not initialized" + + try: + log.info(f"{self.name} client create index : {self.table_name}") + self.admin_cursor.execute(f"USE {self.db_name}") + + # Build vector index attributes + index_param = self.case_config.index_param() + + attrs_json = _build_index_attrs_json(index_param) + + sql = f"CREATE VECTOR INDEX idx_v ON {self.table_name}(v) SECONDARY_ENGINE_ATTRIBUTE='{attrs_json}'" + log.info(f"{self.name} client execute create index: {sql}") + self.admin_cursor.execute(sql) + self.admin_cursor.execute("COMMIT") + except Exception as e: + log.warning(f"Failed to create index on {self.table_name}, error: {e}") + raise e from None + + def insert_embeddings( + self, + embeddings: list[list[float]], + metadata: list[int], + **kwargs, + ) -> tuple[int, Exception | None]: + """Insert a batch via LOAD DATA LOCAL INFILE with sorted PK. Uses the + hex-binary ``UNHEX`` path when the server supports it (probed in + :meth:`init`), otherwise the ``to_vector()`` text path. Requires + self.init() context. + """ + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + + n = len(metadata) + if n == 0: + return 0, None + + tsv_path = Path(tempfile.gettempdir()) / ( + f"vdbb_volc_{self.db_name}_{self.table_name}_{os.getpid()}_{self._batch_counter}.tsv" + ) + self._batch_counter += 1 + + try: + _encode_batch_to_tsv(metadata, embeddings, self.dim, str(tsv_path), binary=self._binary_vec) + + tsv_literal = str(tsv_path).replace("'", "''") + set_clause = "(id, @h) SET v = UNHEX(@h)" if self._binary_vec else "(id, @v) SET v = to_vector(@v)" + load_sql = ( + f"LOAD DATA LOCAL INFILE '{tsv_literal}' " + f"INTO TABLE `{self.db_name}`.`{self.table_name}` " + "FIELDS TERMINATED BY '\\t' LINES TERMINATED BY '\\n' " + f"{set_clause}" + ) + self.cursor.execute(load_sql) + self.conn.commit() + except Exception as e: + log.warning(f"Failed to LOAD DATA into Vector table ({self.table_name}), error: {e}") + return 0, e + else: + actual = self.cursor.rowcount + if actual != n: + # Return the real count so the runner's already_insert_count tracks + # what's actually on disk; on retry it will slice past the + # already-loaded prefix instead of duplicate-PK looping on the + # same batch. self.conn.commit() ran above, so those rows are + # durable. + msg = f"LOAD DATA wrote {actual} rows, expected {n}" + return actual, RuntimeError(msg) + return n, None + finally: + if tsv_path.exists(): + try: + tsv_path.unlink() + except OSError as e: + log.warning(f"Failed to unlink staging TSV {tsv_path}: {e}") + + def prepare_filter(self, filters: Filter): + """Select the filtered vs unfiltered search template for the case. + + Runners apply filters via this hook (called once per case inside the + init() context), not by passing ``filters`` to search_embedding. Store + the predicate value so search_embedding can bind it. + """ + if filters.type == FilterOp.NonFilter: + self._filtered = False + elif filters.type == FilterOp.NumGE: + self._filtered = True + self._filter_value = filters.int_value + else: + msg = f"Unsupported filter for VolcMySQL: {filters}" + raise ValueError(msg) + + def search_embedding( + self, + query: list[float], + k: int = 100, + **kwargs, + ) -> list[int]: + assert self.conn is not None, "Connection is not initialized" + assert self.cursor is not None, "Cursor is not initialized" + + try: + # Binary path: raw LE float32 bytes (C-level struct.pack, ~us) consumed + # by the server as a binary vector via `_binary %s`; avoids the per-query + # str() formatting of 1536 floats and the server-side strtof text parse. + query_param = struct.pack(f"<{len(query)}f", *query) if self._binary_vec else str(query) + if self._filtered: + self.cursor.execute(self.select_sql_with_filter, (self._filter_value, query_param, k)) + else: + self.cursor.execute(self.select_sql, (query_param, k)) + return [row[0] for row in self.cursor.fetchall()] + + except mysql.Error: + log.exception("Failed to execute search query") + raise diff --git a/vectordb_bench/backend/clients/weaviate_cloud/weaviate_cloud.py b/vectordb_bench/backend/clients/weaviate_cloud/weaviate_cloud.py index d6111c8da..453a22296 100644 --- a/vectordb_bench/backend/clients/weaviate_cloud/weaviate_cloud.py +++ b/vectordb_bench/backend/clients/weaviate_cloud/weaviate_cloud.py @@ -7,12 +7,20 @@ import weaviate from weaviate.exceptions import WeaviateBaseError +from vectordb_bench.backend.filter import Filter, FilterOp + from ..api import DBCaseConfig, VectorDB log = logging.getLogger(__name__) class WeaviateCloud(VectorDB): + thread_safe: bool = False + supported_filter_types: list[FilterOp] = [ + FilterOp.NonFilter, + FilterOp.NumGE, + ] + def __init__( self, dim: int, @@ -23,6 +31,7 @@ def __init__( **kwargs, ): """Initialize wrapper around the weaviate vector database.""" + self.name = "WeaviateCloud" db_config.update( { "auth_client_secret": weaviate.AuthApiKey( @@ -37,6 +46,7 @@ def __init__( self._scalar_field = "key" self._vector_field = "vector" self._index_name = "vector_idx" + self._where_filter: dict | None = None # If local setup is used, we if db_config["no_auth"]: @@ -145,16 +155,24 @@ def search_embedding( .with_near_vector({"vector": query}) .with_limit(k) ) - if filters: - where_filter = { - "path": "key", - "operator": "GreaterThanEqual", - "valueInt": filters.get("id"), - } - query_obj = query_obj.with_where(where_filter) + if self._where_filter is not None: + query_obj = query_obj.with_where(self._where_filter) # Perform the search. res = query_obj.do() # Organize results. return [result[self._scalar_field] for result in res["data"]["Get"][self.collection_name]] + + def prepare_filter(self, filters: Filter): + if filters.type == FilterOp.NonFilter: + self._where_filter = None + elif filters.type == FilterOp.NumGE: + self._where_filter = { + "path": [self._scalar_field], + "operator": "GreaterThanEqual", + "valueInt": filters.int_value, + } + else: + msg = f"Unsupported Weaviate filter: {filters}" + raise ValueError(msg) diff --git a/vectordb_bench/backend/clients/zilliz_cloud/cli.py b/vectordb_bench/backend/clients/zilliz_cloud/cli.py index a8d177ee5..030a00661 100644 --- a/vectordb_bench/backend/clients/zilliz_cloud/cli.py +++ b/vectordb_bench/backend/clients/zilliz_cloud/cli.py @@ -13,6 +13,13 @@ ) +def _use_partition_key(parameters: dict) -> bool: + explicit = parameters.get("use_partition_key") + if explicit is not None: + return explicit + return parameters.get("case_type") == "CloudMultiTenantSearchCase" + + class ZillizTypedDict(CommonTypedDict): uri: Annotated[ str, @@ -20,7 +27,7 @@ class ZillizTypedDict(CommonTypedDict): ] user_name: Annotated[ str, - click.option("--user-name", type=str, help="Db username", required=True), + click.option("--user-name", type=str, help="Db username", default=""), ] password: Annotated[ str, @@ -32,6 +39,16 @@ class ZillizTypedDict(CommonTypedDict): show_default="$ZILLIZ_PASSWORD", ), ] + token: Annotated[ + str, + click.option( + "--token", + type=str, + help="Zilliz API token", + default=lambda: os.environ.get("ZILLIZ_TOKEN", ""), + show_default="$ZILLIZ_TOKEN", + ), + ] level: Annotated[ str, click.option("--level", type=str, help="Zilliz index level", required=False), @@ -58,6 +75,17 @@ class ZillizTypedDict(CommonTypedDict): show_default=True, ), ] + use_partition_key: Annotated[ + bool | None, + click.option( + "--use-partition-key/--no-use-partition-key", + default=None, + help=( + "Use the Zilliz Cloud partition key on the label field. " + "Defaults to enabled for CloudMultiTenantSearchCase and disabled otherwise." + ), + ), + ] @cli.command() @@ -72,12 +100,14 @@ def ZillizAutoIndex(**parameters: Unpack[ZillizTypedDict]): uri=SecretStr(parameters["uri"]), user=parameters["user_name"], password=SecretStr(parameters["password"]), + token=SecretStr(parameters["token"]), num_shards=parameters["num_shards"], collection_name=parameters["collection_name"], ), db_case_config=AutoIndexConfig( level=int(parameters["level"]) if parameters["level"] else 1, num_shards=parameters["num_shards"], + use_partition_key=_use_partition_key(parameters), ), **parameters, ) diff --git a/vectordb_bench/backend/clients/zilliz_cloud/config.py b/vectordb_bench/backend/clients/zilliz_cloud/config.py index f0b3fd000..ee5ecf645 100644 --- a/vectordb_bench/backend/clients/zilliz_cloud/config.py +++ b/vectordb_bench/backend/clients/zilliz_cloud/config.py @@ -1,21 +1,27 @@ from pydantic import SecretStr from ..api import DBCaseConfig, DBConfig -from ..milvus.config import IndexType, MilvusIndexConfig +from ..milvus.config import IndexType, MilvusFtsConfig, MilvusIndexConfig class ZillizCloudConfig(DBConfig): uri: SecretStr - user: str - password: SecretStr + user: str = "" + password: SecretStr = SecretStr("") + token: SecretStr = SecretStr("") num_shards: int = 1 collection_name: str = "ZillizCloudVDBBench" + @staticmethod + def common_long_configs() -> list[str]: + return [*DBConfig.common_long_configs(), "user", "password", "token"] + def to_dict(self) -> dict: return { "uri": self.uri.get_secret_value(), "user": self.user, "password": self.password.get_secret_value(), + "token": self.token.get_secret_value(), "num_shards": self.num_shards, "collection_name": self.collection_name, } @@ -40,3 +46,27 @@ def search_param(self) -> dict: "level": self.level, }, } + + +class ZillizCloudFtsConfig(MilvusFtsConfig): + index_type: str = IndexType.AUTOINDEX.value + level: int = 1 + + def sparse_index_param(self) -> dict: + params = {} + if self.bm25_k1 is not None: + params["bm25_k1"] = self.bm25_k1 + if self.bm25_b is not None: + params["bm25_b"] = self.bm25_b + + return { + "index_type": self.index_type, + "metric_type": self.metric_type.value, + "params": params, + } + + def search_param(self) -> dict: + return { + "metric_type": self.metric_type.value, + "params": {"level": self.level}, + } diff --git a/vectordb_bench/backend/data_source.py b/vectordb_bench/backend/data_source.py index 139d2e308..942a80409 100644 --- a/vectordb_bench/backend/data_source.py +++ b/vectordb_bench/backend/data_source.py @@ -1,13 +1,23 @@ import logging +import os import pathlib import typing from abc import ABC, abstractmethod from enum import Enum +import ir_datasets from tqdm import tqdm from vectordb_bench import config +# Set ir_datasets to use tmp directory for both home and temp +# This ensures all downloaded files and temporary data are stored in /tmp +ir_datasets_home = pathlib.Path(config.DATASET_LOCAL_DIR) / "ir_datasets" +ir_datasets_tmp = pathlib.Path(config.DATASET_LOCAL_DIR) / "ir_datasets_tmp" +os.environ.setdefault("IR_DATASETS_HOME", str(ir_datasets_home)) +os.environ.setdefault("IR_DATASETS_TMP", str(ir_datasets_tmp)) + + logging.getLogger("s3fs").setLevel(logging.CRITICAL) log = logging.getLogger(__name__) @@ -18,6 +28,7 @@ class DatasetSource(Enum): S3 = "S3" AliyunOSS = "AliyunOSS" + IR_DATASETS = "IR_DATASETS" def reader(self) -> DatasetReader: if self == DatasetSource.S3: @@ -26,6 +37,9 @@ def reader(self) -> DatasetReader: if self == DatasetSource.AliyunOSS: return AliyunOSSReader() + if self == DatasetSource.IR_DATASETS: + return IRDatasetsReader() + return None @@ -155,3 +169,40 @@ def validate_file(self, remote: pathlib.Path, local: pathlib.Path) -> bool: return False return True + + +class IRDatasetsReader(DatasetReader): + """Reader for ir_datasets based datasets""" + + source: DatasetSource = DatasetSource.IR_DATASETS + remote_root: str = "" # Not used for ir_datasets + + def __init__(self): + self.ir_datasets = ir_datasets + + def read(self, dataset: str, files: list[str], local_ds_root: pathlib.Path): + """ + Download FTS dataset using ir_datasets API + + Args: + dataset: ir_datasets dataset name + files: Expected output files (ignored, not used) + local_ds_root: Local directory (not used, ir_datasets handles its own cache) + """ + log.info(f"Downloading FTS dataset '{dataset}' using ir_datasets") + + try: + # Load dataset using ir_datasets - this will download if needed + # ir_datasets handles caching automatically + # Actual data download happens lazily when iterating + self.ir_datasets.load(dataset) + log.info(f"Successfully loaded dataset: {dataset}") + + except Exception: + log.exception(f"Failed to download FTS dataset '{dataset}'") + raise + + def validate_file(self, remote: pathlib.Path, local: pathlib.Path) -> bool: + """For ir_datasets, we don't validate against remote files""" + # ir_datasets handles its own caching and validation + return local.exists() and local.stat().st_size > 0 diff --git a/vectordb_bench/backend/dataset.py b/vectordb_bench/backend/dataset.py index 12808e420..176c08e17 100644 --- a/vectordb_bench/backend/dataset.py +++ b/vectordb_bench/backend/dataset.py @@ -5,14 +5,22 @@ """ import logging +import math import pathlib +import types +import typing +from abc import ABC, abstractmethod +from collections.abc import Iterator +from dataclasses import dataclass from enum import Enum from typing import Any, ClassVar, NamedTuple +import ir_datasets import pandas as pd import polars as pl from pyarrow.parquet import ParquetFile -from pydantic import field_validator +from pydantic import Field as PydanticField +from pydantic import PrivateAttr, field_validator from vectordb_bench import config from vectordb_bench.base import BaseModel @@ -59,7 +67,7 @@ class BaseDataset(BaseModel): @field_validator("size") @classmethod - def verify_size(cls, v: int) -> int: + def verify_size(cls, v: int): if v not in cls._size_label: msg = f"Size {v} not supported for the dataset, expected: {cls._size_label.keys()}" raise ValueError(msg) @@ -105,7 +113,7 @@ class CustomDataset(BaseDataset): @field_validator("size") @classmethod - def verify_size(cls, v: int) -> int: + def verify_size(cls, v: int): return v @property @@ -122,6 +130,8 @@ def file_count(self) -> int: @property def train_files(self) -> list[str]: + if ("," not in self.train_file) and self.file_num > 1: + return utils.compose_train_files(self.file_num, self.use_shuffled) train_file = self.train_file prefix = f"{train_file}" train_files = [] @@ -138,7 +148,9 @@ class LAION(BaseDataset): metric_type: MetricType = MetricType.L2 use_shuffled: bool = False with_gt: bool = True - _size_label: ClassVar[dict] = { + with_scalar_labels: bool = True + scalar_label_percentages: list[float] = [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5] + _size_label: ClassVar[dict[int, SizeLabel]] = { 100_000_000: SizeLabel(100_000_000, "LARGE", 100), } @@ -148,7 +160,7 @@ class GIST(BaseDataset): dim: int = 960 metric_type: MetricType = MetricType.L2 use_shuffled: bool = False - _size_label: ClassVar[dict] = { + _size_label: ClassVar[dict[int, SizeLabel]] = { 100_000: SizeLabel(100_000, "SMALL", 1), 1_000_000: SizeLabel(1_000_000, "MEDIUM", 1), } @@ -160,7 +172,7 @@ class Cohere(BaseDataset): metric_type: MetricType = MetricType.COSINE use_shuffled: bool = config.USE_SHUFFLED_DATA with_gt: bool = True - _size_label: ClassVar[dict] = { + _size_label: ClassVar[dict[int, SizeLabel]] = { 100_000: SizeLabel(100_000, "SMALL", 1), 1_000_000: SizeLabel(1_000_000, "MEDIUM", 1), 10_000_000: SizeLabel(10_000_000, "LARGE", 10), @@ -198,7 +210,7 @@ class Bioasq(BaseDataset): metric_type: MetricType = MetricType.COSINE use_shuffled: bool = config.USE_SHUFFLED_DATA with_gt: bool = True - _size_label: ClassVar[dict] = { + _size_label: ClassVar[dict[int, SizeLabel]] = { 1_000_000: SizeLabel(1_000_000, "MEDIUM", 1), 10_000_000: SizeLabel(10_000_000, "LARGE", 10), } @@ -234,7 +246,7 @@ class Glove(BaseDataset): dim: int = 200 metric_type: MetricType = MetricType.COSINE use_shuffled: bool = False - _size_label: ClassVar[dict] = {1_000_000: SizeLabel(1_000_000, "MEDIUM", 1)} + _size_label: ClassVar[dict[int, SizeLabel]] = {1_000_000: SizeLabel(1_000_000, "MEDIUM", 1)} class SIFT(BaseDataset): @@ -242,7 +254,7 @@ class SIFT(BaseDataset): dim: int = 128 metric_type: MetricType = MetricType.L2 use_shuffled: bool = False - _size_label: ClassVar[dict] = { + _size_label: ClassVar[dict[int, SizeLabel]] = { 500_000: SizeLabel( 500_000, "SMALL", @@ -259,7 +271,7 @@ class OpenAI(BaseDataset): metric_type: MetricType = MetricType.COSINE use_shuffled: bool = config.USE_SHUFFLED_DATA with_gt: bool = True - _size_label: ClassVar[dict] = { + _size_label: ClassVar[dict[int, SizeLabel]] = { 50_000: SizeLabel(50_000, "SMALL", 1), 500_000: SizeLabel(500_000, "MEDIUM", 1), 5_000_000: SizeLabel(5_000_000, "LARGE", 10), @@ -338,11 +350,16 @@ def data_dir(self) -> pathlib.Path: def __iter__(self): return DataSetIterator(self) + def iter_batches(self, batch_size: int): + return DataSetIterator(self, batch_size=batch_size) + # TODO passing use_shuffle from outside def prepare( self, source: DatasetSource = DatasetSource.S3, filters: Filter = non_filter, + with_train_files: bool = True, + with_scalar_labels: bool = False, ) -> bool: """Download the dataset from DatasetSource url = f"{source}/{self.data.dir_name}" @@ -356,7 +373,7 @@ def prepare( bool: whether the dataset is successfully prepared """ - self.train_files = self.data.train_files + self.train_files = self.data.train_files if with_train_files else [] gt_file, test_file = None, None if self.data.with_gt: gt_file, test_file = filters.groundtruth_file, self.data.test_file @@ -373,12 +390,10 @@ def prepare( local_ds_root=self.data_dir, ) + needs_scalar_labels = filters.type == FilterOp.StrEqual or with_scalar_labels + # read scalar_labels_file if separated - if ( - filters.type == FilterOp.StrEqual - and self.data.with_scalar_labels - and self.data.scalar_labels_file_separated - ): + if needs_scalar_labels and self.data.with_scalar_labels and self.data.scalar_labels_file_separated: self.scalar_labels = self._read_file(self.data.scalar_labels_file) if gt_file is not None and test_file is not None: @@ -401,8 +416,9 @@ def _read_file(self, file_name: str) -> pl.DataFrame: class DataSetIterator: - def __init__(self, dataset: DatasetManager): + def __init__(self, dataset: DatasetManager, batch_size: int = config.NUM_PER_BATCH): self._ds = dataset + self._batch_size = batch_size self._idx = 0 # file number self._cur = None self._sub_idx = [0 for i in range(len(self._ds.train_files))] # iter num for each file @@ -428,7 +444,7 @@ def _get_iter(self, file_name: str): msg = f"No such file: {p}" log.warning(msg) raise IndexError(msg) - return ParquetFile(p, memory_map=True, pre_buffer=True).iter_batches(config.NUM_PER_BATCH) + return ParquetFile(p, memory_map=True, pre_buffer=True).iter_batches(self._batch_size) def __next__(self) -> pd.DataFrame: """return the data in the next file of the training list""" @@ -478,6 +494,7 @@ class DatasetWithSizeType(Enum): CohereSmall = "Small Cohere (768dim, 100K)" CohereMedium = "Medium Cohere (768dim, 1M)" CohereLarge = "Large Cohere (768dim, 10M)" + LAIONLarge = "Large LAION (768dim, 100M)" BioasqMedium = "Medium Bioasq (1024dim, 1M)" BioasqLarge = "Large Bioasq (1024dim, 10M)" OpenAISmall = "Small OpenAI (1536dim, 50K)" @@ -491,6 +508,8 @@ def get_manager(self) -> DatasetManager: return DatasetWithSizeMap.get(self) def get_load_timeout(self) -> float: + if self is DatasetWithSizeType.LAIONLarge: + return config.LOAD_TIMEOUT_768D_100M if "small" in self.value.lower(): return config.LOAD_TIMEOUT_768D_100K if "medium" in self.value.lower(): @@ -501,6 +520,8 @@ def get_load_timeout(self) -> float: raise KeyError(msg) def get_optimize_timeout(self) -> float: + if self is DatasetWithSizeType.LAIONLarge: + return config.OPTIMIZE_TIMEOUT_768D_100M if "small" in self.value.lower(): return config.OPTIMIZE_TIMEOUT_768D_100K if "medium" in self.value.lower(): @@ -514,9 +535,668 @@ def get_optimize_timeout(self) -> float: DatasetWithSizeType.CohereSmall: Dataset.COHERE.manager(100_000), DatasetWithSizeType.CohereMedium: Dataset.COHERE.manager(1_000_000), DatasetWithSizeType.CohereLarge: Dataset.COHERE.manager(10_000_000), + DatasetWithSizeType.LAIONLarge: Dataset.LAION.manager(100_000_000), DatasetWithSizeType.BioasqMedium: Dataset.BIOASQ.manager(1_000_000), DatasetWithSizeType.BioasqLarge: Dataset.BIOASQ.manager(10_000_000), DatasetWithSizeType.OpenAISmall: Dataset.OPENAI.manager(50_000), DatasetWithSizeType.OpenAIMedium: Dataset.OPENAI.manager(500_000), DatasetWithSizeType.OpenAILarge: Dataset.OPENAI.manager(5_000_000), } + + +# FTS Dataset Translator Pattern +@dataclass +class FtsQuery: + """Internal representation of an FTS query.""" + + query_id: str + text: str + + +@dataclass +class FtsDocument: + """Internal representation of an FTS document.""" + + doc_id: str + text: str + filter_id: int | None = None + + +_FTS_FILTER_GOLDEN_RATIO_64 = 0x9E3779B97F4A7C15 +_FTS_FILTER_OFFSET_SEED = 0xD1B54A32D192ED03 + + +@dataclass(frozen=True) +class FtsFilterIdPermutation: + """Deterministic bijection that scatters FTS filter IDs across corpus order.""" + + size: int + multiplier: int + offset: int + + @property + def algorithm(self) -> str: + return "affine_permutation_v1" + + @classmethod + def for_size(cls, size: int) -> "FtsFilterIdPermutation": + if size <= 0: + msg = f"FTS filter ID permutation size must be positive, got {size}" + raise ValueError(msg) + if size == 1: + return cls(size=1, multiplier=1, offset=0) + + multiplier = max(1, (size * _FTS_FILTER_GOLDEN_RATIO_64) >> 64) + while math.gcd(multiplier, size) != 1: + multiplier += 1 + if multiplier >= size: + multiplier = 1 + + return cls( + size=size, + multiplier=multiplier, + offset=_FTS_FILTER_OFFSET_SEED % size, + ) + + def map(self, ordinal: int) -> int: + if ordinal < 0 or ordinal >= self.size: + msg = f"FTS filter ID ordinal must be in [0, {self.size}), got {ordinal}" + raise ValueError(msg) + return (self.multiplier * ordinal + self.offset) % self.size + + +class FtsDatasetTranslator(ABC): + """Abstract base class for converting ir_datasets schema to internal format. + + This translator pattern allows easy extension to support new datasets + (BEIR, TREC, etc.) without modifying core code. + """ + + @property + @abstractmethod + def ir_datasets_name(self) -> str: + """Return the ir_datasets dataset name. + + Example: 'msmarco-passage/dev/small' + """ + + @abstractmethod + def translate_query(self, ir_query: typing.Any) -> FtsQuery: + """Convert ir_datasets query to internal FtsQuery format.""" + + @abstractmethod + def translate_document(self, ir_doc: typing.Any) -> FtsDocument: + """Convert ir_datasets document to internal FtsDocument format.""" + + def load(self) -> typing.Any: + """Load ir_datasets dataset.""" + return ir_datasets.load(self.ir_datasets_name) + + def iter_queries(self, dataset: typing.Any) -> Iterator[FtsQuery]: + """Iterate over queries in the dataset.""" + for q in dataset.queries_iter(): + yield self.translate_query(q) + + def iter_documents(self, dataset: typing.Any) -> Iterator[FtsDocument]: + """Iterate over documents in the dataset.""" + for doc in dataset.docs_iter(): + yield self.translate_document(doc) + + def load_ground_truth(self, dataset: typing.Any) -> dict[str, dict[str, int]]: + """Load positive semantic qrels keyed by query id. + + ir_datasets qrels may contain non-positive judgments. Those are not + relevant documents for recall/MRR/NDCG, so they are ignored here. + """ + qrels: dict[str, dict[str, int]] = {} + for qrel in dataset.qrels_iter(): + relevance = int(getattr(qrel, "relevance", 0)) + if relevance <= 0: + continue + query_id = str(qrel.query_id) + doc_id = str(qrel.doc_id) + qrels.setdefault(query_id, {})[doc_id] = max( + relevance, + qrels.get(query_id, {}).get(doc_id, 0), + ) + return qrels + + +class MSMarcoTranslator(FtsDatasetTranslator): + """Translator for MS MARCO passage retrieval dataset.""" + + @property + def ir_datasets_name(self) -> str: + return "msmarco-passage/dev/small" + + def translate_query(self, ir_query: typing.Any) -> FtsQuery: + return FtsQuery(query_id=str(ir_query.query_id), text=ir_query.text) + + def translate_document(self, ir_doc: typing.Any) -> FtsDocument: + clean_text = ir_doc.text.replace("\t", " ").replace("\n", " ") + return FtsDocument(doc_id=str(ir_doc.doc_id), text=clean_text) + + +class HotpotQATranslator(FtsDatasetTranslator): + """Translator for BEIR HotpotQA.""" + + @property + def ir_datasets_name(self) -> str: + return "beir/hotpotqa/test" + + def translate_query(self, ir_query: typing.Any) -> FtsQuery: + return FtsQuery(query_id=str(ir_query.query_id), text=ir_query.text) + + def translate_document(self, ir_doc: typing.Any) -> FtsDocument: + title = getattr(ir_doc, "title", "") or "" + text = getattr(ir_doc, "text", "") or "" + clean_text = f"{title} {text}".replace("\t", " ").replace("\n", " ").strip() + return FtsDocument(doc_id=str(ir_doc.doc_id), text=clean_text) + + +class FtsBaseDataset(BaseModel): + """Base class for FTS datasets - completely independent from BaseDataset. + + FTS datasets are text-based and use TSV files instead of parquet files. + They don't have vector dimensions; native full-text search uses BM25. + + """ + + name: str + size: int + metric_type: MetricType = MetricType.BM25 + with_gt: bool = True + with_remote_resource: bool = False + gt_neighbors_field: str = "neighbors_id" + + _size_label: ClassVar[dict[int, SizeLabel]] + + @field_validator("size") + @classmethod + def verify_size(cls, v: int): + if v not in cls._size_label: + msg = f"Size {v} not supported for the FTS dataset, expected: {cls._size_label.keys()}" + raise ValueError(msg) + return v + + @property + def label(self) -> str: + """Get size label (SMALL, MEDIUM, LARGE, etc.)""" + return self._size_label.get(self.size).label + + @property + def full_name(self) -> str: + return f"{self.name} FTS ({self.label})" + + @property + def dir_name(self) -> str: + return f"{self.name}_{self.label}_{utils.numerize(self.size)}".lower() + + +class MSMarcoFts(FtsBaseDataset): + name: str = "MS MARCO" + with_gt: bool = True + with_remote_resource: bool = False + + _size_label: ClassVar[dict[int, SizeLabel]] = { + 100_000: SizeLabel(100_000, "SMALL", 1), + 1_000_000: SizeLabel(1_000_000, "MEDIUM", 1), + 8_841_823: SizeLabel(8_841_823, "LARGE", 1), + } + + @property + def dir_name(self) -> str: + return f"msmarco_{self.label}_{utils.numerize(self.size)}".lower() + + +class HotpotQAFts(FtsBaseDataset): + name: str = "HotpotQA" + with_gt: bool = True + with_remote_resource: bool = False + + _size_label: ClassVar[dict[int, SizeLabel]] = { + 100_000: SizeLabel(100_000, "SMALL", 1), + 1_000_000: SizeLabel(1_000_000, "MEDIUM", 1), + 5_233_329: SizeLabel(5_233_329, "LARGE", 1), + } + + +class FtsDatasetManager(BaseModel): + """Manager for FTS datasets - independent from DatasetManager. + + Handles FTS dataset preparation using Translator pattern for extensibility. + + Similar to DatasetManager, but for text-based FTS datasets: + - queries_data: loaded queries (similar to test_data in vectors) + - gt_data: loaded ground truth (similar to gt_data in vectors) + - recall_queries_data: recall-valid queries after optional FTS filter + - recall_gt_data: recall-valid ground truth after optional FTS filter + - translator: dataset-specific translator for schema conversion + - _ir_dataset: ir_datasets dataset object for direct access + """ + + data: FtsBaseDataset + _translator: typing.Any = PrivateAttr() + + queries_data: list[FtsQuery] | None = None + gt_data: list[dict[str, int]] | None = None + recall_queries_data: list[FtsQuery] | None = None + recall_gt_data: list[dict[str, int]] | None = None + recall_skipped: bool = False + recall_skip_reason: str | None = None + qrels_data: dict[str, dict[str, int]] = PydanticField(default_factory=dict) + required_doc_ids: set[str] = PydanticField(default_factory=set) + selected_doc_ids: set[str] | None = None + qrel_filter_ids: dict[str, int] = PydanticField(default_factory=dict) + filter_stats: dict[str, int | float | str] = PydanticField(default_factory=dict) + _ir_dataset: typing.Any = PrivateAttr(default=None) + + def __init__(self, **data): + super().__init__(**data) + # Initialize translator based on dataset name + if isinstance(self.data, MSMarcoFts): + self._translator = MSMarcoTranslator() + elif isinstance(self.data, HotpotQAFts): + self._translator = HotpotQATranslator() + else: + msg = f"No translator available for dataset: {self.data.name}" + raise TypeError(msg) + + def __eq__(self, obj: any): + if isinstance(obj, FtsDatasetManager): + return self.data.name == obj.data.name and self.data.size == obj.data.size + return False + + def __hash__(self) -> int: + return hash((self.data.name, self.data.size)) + + @property + def data_dir(self) -> pathlib.Path: + """Get local data directory for this FTS dataset, following vector dataset structure""" + return pathlib.Path( + config.DATASET_LOCAL_DIR, + self.data.name.lower(), + self.data.dir_name, + ) + + def _validate_cap(self, required_doc_ids: set[str], target_size: int) -> None: + if len(required_doc_ids) > target_size: + msg = ( + f"{self.data.full_name} size={target_size} is too small for semantic qrels; " + f"requires {len(required_doc_ids)} qrel documents" + ) + raise ValueError(msg) + + def _build_selected_doc_ids(self) -> set[str]: + """Select the capped corpus while preserving every positive qrel doc.""" + if self._ir_dataset is None: + msg = "ir_datasets dataset not loaded. Call prepare() first." + raise RuntimeError(msg) + + required_doc_ids = set(self.required_doc_ids) + self._validate_cap(required_doc_ids=required_doc_ids, target_size=self.data.size) + + selected_doc_ids = set(required_doc_ids) + found_required_doc_ids: set[str] = set() + for doc in self._translator.iter_documents(self._ir_dataset): + doc_id = str(doc.doc_id) + if doc_id in required_doc_ids: + found_required_doc_ids.add(doc_id) + + if doc_id not in selected_doc_ids and len(selected_doc_ids) < self.data.size: + selected_doc_ids.add(doc_id) + + if len(selected_doc_ids) >= self.data.size and found_required_doc_ids == required_doc_ids: + break + + missing_doc_ids = required_doc_ids - found_required_doc_ids + if missing_doc_ids: + preview = ", ".join(sorted(missing_doc_ids)[:10]) + msg = ( + f"{self.data.full_name} semantic qrel docs missing from corpus: {preview}" + f"{'...' if len(missing_doc_ids) > 10 else ''}" + ) + raise ValueError(msg) + + return selected_doc_ids + + def _iter_selected_documents_with_filter_ids(self) -> Iterator[FtsDocument]: + """Yield selected documents with the exact filter IDs used for insertion and qrels.""" + if self._ir_dataset is None: + msg = "ir_datasets dataset not loaded. Call prepare() first." + raise RuntimeError(msg) + + permutation = FtsFilterIdPermutation.for_size(self.data.size) + documents = iter(self._translator.iter_documents(self._ir_dataset)) + emitted_count = 0 + while emitted_count < self.data.size: + try: + doc = next(documents) + doc.doc_id = str(doc.doc_id) + if self.selected_doc_ids is not None and doc.doc_id not in self.selected_doc_ids: + continue + doc.filter_id = permutation.map(emitted_count) + except StopIteration: + break + except Exception as e: + log.debug(f"Skipping malformed document: {e}") + continue + + emitted_count += 1 + yield doc + + def _build_qrel_filter_ids(self) -> dict[str, int]: + """Map qrel doc IDs to their deterministic permuted FTS filter ID.""" + if self.selected_doc_ids is None: + msg = "selected_doc_ids is required before building FTS filter IDs" + raise RuntimeError(msg) + + qrel_doc_ids = set(self.required_doc_ids) + qrel_filter_ids: dict[str, int] = {} + for doc in self._iter_selected_documents_with_filter_ids(): + doc_id = doc.doc_id + if doc_id in qrel_doc_ids: + qrel_filter_ids[doc_id] = doc.filter_id + + missing_doc_ids = qrel_doc_ids - set(qrel_filter_ids) + if missing_doc_ids: + preview = ", ".join(sorted(missing_doc_ids)[:10]) + msg = ( + f"{self.data.full_name} semantic qrel docs missing filter_id assignment: {preview}" + f"{'...' if len(missing_doc_ids) > 10 else ''}" + ) + raise ValueError(msg) + return qrel_filter_ids + + def _apply_integer_filter_to_qrels( + self, + queries: list[FtsQuery], + ground_truth: list[dict[str, int]], + filters: Filter, + ) -> tuple[list[FtsQuery], list[dict[str, int]]]: + filter_field = getattr(filters, "int_field", "filter_id") + if filter_field != "filter_id": + msg = f"FTS integer filters require int_field='filter_id', got {filter_field!r}" + raise ValueError(msg) + + filter_value = int(filters.int_value) + if filter_value < 0 or filter_value > self.data.size: + msg = f"FTS filter_id threshold must be in [0, {self.data.size}], got {filter_value}" + raise ValueError(msg) + + self.qrel_filter_ids = self._build_qrel_filter_ids() + filtered_queries: list[FtsQuery] = [] + filtered_gt: list[dict[str, int]] = [] + for query, qrels in zip(queries, ground_truth, strict=True): + filtered_qrels = { + doc_id: rel for doc_id, rel in qrels.items() if self.qrel_filter_ids.get(doc_id, -1) >= filter_value + } + if not filtered_qrels: + continue + filtered_queries.append(query) + filtered_gt.append(filtered_qrels) + + matched_doc_count = self.data.size - filter_value + filtered_relevant_doc_ids = {doc_id for qrels in filtered_gt for doc_id in qrels} + permutation = FtsFilterIdPermutation.for_size(self.data.size) + self.filter_stats = { + "filter_type": filters.type.value, + "filter_field": filter_field, + "filter_value": filter_value, + "filter_rate": filters.filter_rate, + "filter_id_distribution": permutation.algorithm, + "filter_id_multiplier": permutation.multiplier, + "filter_id_offset": permutation.offset, + "matched_doc_count": matched_doc_count, + "matched_doc_ratio": round(matched_doc_count / self.data.size, 6), + "original_query_count": len(queries), + "filtered_query_count": len(filtered_queries), + "filtered_query_ratio": round(len(filtered_queries) / len(queries), 6), + "original_relevant_doc_count": len(self.required_doc_ids), + "filtered_relevant_doc_count": len(filtered_relevant_doc_ids), + } + log.info( + "Applied FTS integer filter %s >= %s: queries %s/%s, relevant docs %s/%s", + filter_field, + filter_value, + len(filtered_queries), + len(queries), + len(filtered_relevant_doc_ids), + len(self.required_doc_ids), + ) + if not filtered_queries: + self.recall_skipped = True + self.recall_skip_reason = "no_positive_qrels_after_filter" + return filtered_queries, filtered_gt + + def _apply_filters_to_qrels( + self, + queries: list[FtsQuery], + ground_truth: list[dict[str, int]], + filters: Filter | None, + ) -> tuple[list[FtsQuery], list[dict[str, int]]]: + self.filter_stats = {} + self.qrel_filter_ids = {} + self.recall_skipped = False + self.recall_skip_reason = None + if filters is None or filters.type == FilterOp.NonFilter: + return queries, ground_truth + if filters.type == FilterOp.NumGE: + return self._apply_integer_filter_to_qrels(queries, ground_truth, filters) + msg = f"FTS dataset filtering does not support filter type {filters.type}" + raise ValueError(msg) + + def prepare( + self, + source: DatasetSource | None = None, + filters: Filter | None = None, + ) -> bool: + """Prepare FTS dataset for testing using Translator pattern. + + Directly uses ir_datasets API without generating TSV files: + 1. Downloads dataset using ir_datasets (if needed) + 2. Loads dataset object using translator + 3. Loads queries and semantic qrels from ir_datasets + + Args: + source: Data source to download from (should be IR_DATASETS for FTS) + filters: Optional filters. FTS supports natural semantic GT + filtering for integer filter_id cases. + + Returns: + bool: True if preparation successful, False otherwise + """ + log.info(f"Preparing FTS dataset: {self.data.full_name}") + + try: + # Download dataset if needed (ir_datasets handles caching) + if source is not None: + reader = source.reader() + if reader is not None: + dataset_name = self._translator.ir_datasets_name + # reader.read() will download the dataset if needed + reader.read(dataset_name, [], self.data_dir) + + # Load dataset using translator + self._ir_dataset = self._translator.load() + log.info(f"Successfully loaded ir_datasets dataset: {self._translator.ir_datasets_name}") + + # Load queries from ir_datasets and semantic ground truth by query id. + if self.data.with_gt: + all_queries = list(self._translator.iter_queries(self._ir_dataset)) + log.info(f"Loaded {len(all_queries)} queries into memory") + + self.qrels_data = self._translator.load_ground_truth(self._ir_dataset) + self.queries_data = [] + self.gt_data = [] + for query in all_queries: + qrels = self.qrels_data.get(query.query_id) + if not qrels: + continue + self.queries_data.append( + FtsQuery( + query_id=query.query_id, + text=query.text, + ) + ) + self.gt_data.append(qrels) + + if not self.queries_data: + msg = f"{self.data.full_name} has no queries with positive semantic qrels" + raise ValueError(msg) # noqa: TRY301 + + self.required_doc_ids = {doc_id for qrels in self.gt_data for doc_id in qrels} + self.selected_doc_ids = self._build_selected_doc_ids() + self.recall_queries_data, self.recall_gt_data = self._apply_filters_to_qrels( + self.queries_data, + self.gt_data, + filters, + ) + log.info( + "Loaded semantic qrels for %s queries; recall uses %s queries; " + "selected %s corpus docs including %s qrel docs", + len(self.gt_data), + len(self.recall_gt_data), + len(self.selected_doc_ids), + len(self.required_doc_ids), + ) + else: + self.selected_doc_ids = None + self.qrel_filter_ids = {} + self.filter_stats = {} + self.recall_queries_data = None + self.recall_gt_data = None + self.recall_skipped = False + self.recall_skip_reason = None + + except (TypeError, ValueError): + log.exception("Invalid FTS dataset configuration") + raise + except Exception: + log.exception("Failed to prepare FTS dataset") + return False + else: + log.debug(f"{self.data.name}: FTS dataset prepared") + log.info(f"FTS dataset preparation completed: {self.data.full_name}") + return True + + def iter_batches(self, batch_size: int = config.NUM_PER_BATCH): + """Return an iterator for streaming FTS document batches.""" + return FtsDocumentIterator(self, batch_size=batch_size) + + def __iter__(self): + """Return iterator for streaming document batches. + + Similar to DatasetManager.__iter__() which returns DataSetIterator. + This enables batch-by-batch processing of documents without loading + all documents into memory at once. + + Example: + >>> manager = FtsDataset.MSMARCO.manager(100_000) + >>> for batch in manager: + >>> print(f"Processing {len(batch)} documents") + """ + return self.iter_batches() + + +class FtsDocumentIterator: + """Iterator for streaming FTS document batches using Translator pattern. + + Similar to DataSetIterator for vector datasets, but reads directly from ir_datasets + using translator. Yields batches of FtsDocument objects for memory-efficient + processing of large datasets. + """ + + def __init__(self, dataset: FtsDatasetManager, batch_size: int = config.NUM_PER_BATCH): + self._ds = dataset + self._batch_size = batch_size + self._finished = False + self._docs_iter = None + + def __iter__(self): + return self + + def __next__(self) -> list[FtsDocument]: + """Return the next batch of documents. + + Returns: + list[FtsDocument]: List of FtsDocument objects + + Raises: + StopIteration: When all documents have been read + """ + if self._finished: + raise StopIteration + + if self._docs_iter is None: + self._docs_iter = self._ds._iter_selected_documents_with_filter_ids() + + batch = [] + while len(batch) < self._batch_size: + try: + batch.append(next(self._docs_iter)) + except StopIteration: + self._finished = True + if batch: + return batch + raise + return batch + + def __enter__(self): + """Enter context manager.""" + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> None: + """Exit context manager.""" + + def __del__(self): + """Cleanup when iterator is destroyed.""" + + +class FtsDataset(Enum): + MSMARCO = MSMarcoFts + HOTPOTQA = HotpotQAFts + + def get(self, size: int) -> FtsBaseDataset: + return self.value(size=size) + + def manager(self, size: int) -> FtsDatasetManager: + return FtsDatasetManager(data=self.get(size)) + + +class FtsDatasetWithSizeType(Enum): + MSMarcoSmall = "MS MARCO Small (100K documents)" + MSMarcoMedium = "MS MARCO Medium (1M documents)" + MSMarcoLarge = "MS MARCO Large (8.8M documents)" + HotpotQASmall = "HotpotQA Small (100K documents)" + HotpotQAMedium = "HotpotQA Medium (1M documents)" + HotpotQALarge = "HotpotQA Large (5.2M documents)" + + def get_manager(self) -> FtsDatasetManager: + return { + FtsDatasetWithSizeType.MSMarcoSmall: FtsDataset.MSMARCO.manager(100_000), + FtsDatasetWithSizeType.MSMarcoMedium: FtsDataset.MSMARCO.manager(1_000_000), + FtsDatasetWithSizeType.MSMarcoLarge: FtsDataset.MSMARCO.manager(8_841_823), + FtsDatasetWithSizeType.HotpotQASmall: FtsDataset.HOTPOTQA.manager(100_000), + FtsDatasetWithSizeType.HotpotQAMedium: FtsDataset.HOTPOTQA.manager(1_000_000), + FtsDatasetWithSizeType.HotpotQALarge: FtsDataset.HOTPOTQA.manager(5_233_329), + }[self] + + def get_load_timeout(self) -> float: + if self in {FtsDatasetWithSizeType.MSMarcoSmall, FtsDatasetWithSizeType.HotpotQASmall}: + return config.LOAD_TIMEOUT_768D_100K + return config.LOAD_TIMEOUT_DEFAULT + + def get_optimize_timeout(self) -> float: + if self in {FtsDatasetWithSizeType.MSMarcoSmall, FtsDatasetWithSizeType.HotpotQASmall}: + return config.OPTIMIZE_TIMEOUT_768D_100K + return config.OPTIMIZE_TIMEOUT_DEFAULT + + @property + def is_advanced(self) -> bool: + return self in {FtsDatasetWithSizeType.MSMarcoLarge, FtsDatasetWithSizeType.HotpotQALarge} diff --git a/vectordb_bench/backend/payload.py b/vectordb_bench/backend/payload.py new file mode 100644 index 000000000..04cba3b85 --- /dev/null +++ b/vectordb_bench/backend/payload.py @@ -0,0 +1,25 @@ +from enum import StrEnum + + +class PayloadProfile(StrEnum): + IDS_ONLY = "ids_only" + VECTOR = "vector" + SCALAR_LABEL = "scalar_label" + TEXT = "text" + + def estimated_bytes_per_query(self, *, k: int, dim: int) -> int: + # Approximate payload size used for cloud leaderboard cost expansion. + # ID + distance is about 20 bytes per hit; vector is float32. + id_distance_bytes = 20 + scalar_label_bytes = 16 + if self == PayloadProfile.IDS_ONLY: + return k * id_distance_bytes + if self == PayloadProfile.VECTOR: + return k * (id_distance_bytes + dim * 4) + if self == PayloadProfile.SCALAR_LABEL: + return k * (id_distance_bytes + scalar_label_bytes) + if self == PayloadProfile.TEXT: + # Approximate returned document text. FTS metrics still use IDs only. + return k * (id_distance_bytes + 512) + msg = f"Unsupported payload profile: {self}" + raise ValueError(msg) diff --git a/vectordb_bench/backend/result_collector.py b/vectordb_bench/backend/result_collector.py index a4119a5fd..a550df27a 100644 --- a/vectordb_bench/backend/result_collector.py +++ b/vectordb_bench/backend/result_collector.py @@ -1,26 +1,128 @@ +import argparse import logging import pathlib +import sys +from datetime import date +from typing import Literal from vectordb_bench.models import TestResult log = logging.getLogger(__name__) +ResultGroupBy = Literal["run_id", "db"] + class ResultCollector: + @staticmethod + def _group_key(file_result: TestResult, group_by: ResultGroupBy) -> str: + if group_by == "run_id": + return file_result.run_id + + if not file_result.results: + return file_result.run_id + return file_result.results[0].task_config.db.value + @classmethod - def collect(cls, result_dir: pathlib.Path) -> list[TestResult]: + def collect( + cls, + result_dir: pathlib.Path, + group_by: ResultGroupBy = "run_id", + trans_unit: bool = True, + ) -> list[TestResult]: reg = "result_*.json" results_d = {} + if group_by not in {"run_id", "db"}: + msg = f"Unsupported result grouping: {group_by}" + raise ValueError(msg) + if not result_dir.exists() or len(list(result_dir.rglob(reg))) == 0: return [] - for json_file in result_dir.rglob(reg): - file_result = TestResult.read_file(json_file, trans_unit=True) + for json_file in sorted(result_dir.rglob(reg)): + file_result = TestResult.read_file(json_file, trans_unit=trans_unit) + key = cls._group_key(file_result, group_by) - # Group result files of the same run_id into one TestResult - if file_result.run_id in results_d: - results_d[file_result.run_id].results.extend(file_result.results) + # Default behavior groups files from the same run. FTS publish artifacts can + # opt into DB grouping so one backend owns one consolidated result file. + if key in results_d: + results_d[key].results.extend(file_result.results) else: - results_d[file_result.run_id] = file_result + results_d[key] = file_result return list(results_d.values()) + + @classmethod + def merge_by_db( + cls, + result_dir: pathlib.Path, + task_label: str = "fts_standard", + replace: bool = False, + dry_run: bool = False, + ) -> list[pathlib.Path]: + source_files = sorted(result_dir.rglob("result_*.json")) + merged_results = cls.collect(result_dir, group_by="db", trans_unit=False) + output_files = [] + + for result in merged_results: + if not result.results: + continue + + db = result.results[0].task_config.db.value + db_lower = db.lower() + merged = TestResult( + run_id=f"{task_label}_{db_lower}", + task_label=task_label, + results=result.results, + timestamp=result.timestamp, + ) + file_name = merged.file_fmt.format(date.today().strftime("%Y%m%d"), task_label, db_lower) + output_file = result_dir.joinpath(db, file_name) + output_files.append(output_file) + + if dry_run: + log.info("Would write %s with %s case results", output_file, len(result.results)) + else: + merged.write_db_file(result_dir.joinpath(db), merged, db_lower) + + if replace and not dry_run: + output_file_set = set(output_files) + for source_file in source_files: + if source_file not in output_file_set and source_file.exists(): + source_file.unlink() + + return output_files + + +def _parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Collect or consolidate VDBBench result JSON files.") + parser.add_argument("result_dir", type=pathlib.Path) + parser.add_argument("--merge-by-db", action="store_true", help="write one consolidated result file per backend") + parser.add_argument( + "--replace", action="store_true", help="remove source result files after merged files are written" + ) + parser.add_argument("--task-label", default="fts_standard", help="task label used for merged result files") + parser.add_argument("--dry-run", action="store_true", help="show planned output files without writing") + return parser.parse_args() + + +def main(): + args = _parse_args() + logging.basicConfig(level=logging.INFO, format="%(levelname)s:%(name)s:%(message)s") + + if args.merge_by_db: + output_files = ResultCollector.merge_by_db( + args.result_dir, + task_label=args.task_label, + replace=args.replace, + dry_run=args.dry_run, + ) + for output_file in output_files: + sys.stdout.write(f"{output_file}\n") + return + + for result in ResultCollector.collect(args.result_dir): + sys.stdout.write(f"{result.run_id}\t{len(result.results)}\n") + + +if __name__ == "__main__": + main() diff --git a/vectordb_bench/backend/runner/__init__.py b/vectordb_bench/backend/runner/__init__.py index 4af583773..ddee99554 100644 --- a/vectordb_bench/backend/runner/__init__.py +++ b/vectordb_bench/backend/runner/__init__.py @@ -1,8 +1,12 @@ +from .cold_warm_runner import ColdWarmSearchRunner +from .concurrent_runner import ConcurrentInsertRunner from .mp_runner import MultiProcessingSearchRunner from .read_write_runner import ReadWriteRunner from .serial_runner import SerialInsertRunner, SerialSearchRunner __all__ = [ + "ColdWarmSearchRunner", + "ConcurrentInsertRunner", "MultiProcessingSearchRunner", "ReadWriteRunner", "SerialInsertRunner", diff --git a/vectordb_bench/backend/runner/cold_warm_runner.py b/vectordb_bench/backend/runner/cold_warm_runner.py new file mode 100644 index 000000000..51bde6ff8 --- /dev/null +++ b/vectordb_bench/backend/runner/cold_warm_runner.py @@ -0,0 +1,120 @@ +import logging +import time + +import numpy as np + +from vectordb_bench.backend.filter import Filter, non_filter +from vectordb_bench.backend.payload import PayloadProfile + +from ... import config +from ..clients import api + +log = logging.getLogger(__name__) + + +class ColdWarmSearchRunner: + def __init__( + self, + db: api.VectorDB, + test_data: list[list[float]], + k: int = 100, + filters: Filter = non_filter, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + query_count: int = 1000, + ): + if query_count <= 0: + msg = "query_count must be positive" + raise ValueError(msg) + if len(test_data) < query_count: + msg = f"query_count={query_count} exceeds test_data size={len(test_data)}" + raise ValueError(msg) + + self.db = db + self.k = k + self.filters = filters + self.payload_profile = payload_profile + self.query_count = query_count + if not self.db.supports_payload_profile(self.payload_profile): + msg = f"{self.db.name} does not support payload_profile={self.payload_profile.value}" + raise NotImplementedError(msg) + + self.test_data = [ + query.tolist() if isinstance(query, np.ndarray) else query for query in test_data[:query_count] + ] + + def _search_embedding(self, emb: list[float]) -> list[int]: + if self.payload_profile == PayloadProfile.IDS_ONLY: + return self.db.search_embedding(emb, self.k) + return self.db.search_embedding(emb, self.k, payload_profile=self.payload_profile) + + def _get_db_search_res(self, emb: list[float], retry_idx: int = 0) -> list[int]: + try: + results = self._search_embedding(emb) + except Exception as e: + log.warning(f"Cold/warm search failed, retry_idx={retry_idx}, Exception: {e}") + if retry_idx < config.MAX_SEARCH_RETRY: + return self._get_db_search_res(emb=emb, retry_idx=retry_idx + 1) + + msg = f"Cold/warm search failed and retried more than {config.MAX_SEARCH_RETRY} times" + raise RuntimeError(msg) from e + + return results + + @staticmethod + def _latency_stats(latencies: list[float]) -> dict[str, float]: + return { + "first_query_latency": round(float(latencies[0]), 4), + "p99_latency": round(float(np.percentile(latencies, 99)), 4), + "p95_latency": round(float(np.percentile(latencies, 95)), 4), + "avg_latency": round(float(np.mean(latencies)), 4), + } + + @staticmethod + def _safe_ratio(numerator: float, denominator: float) -> float: + if denominator == 0: + return 0.0 + return round(float(numerator / denominator), 4) + + def _ratio_stats(self, cold_stats: dict[str, float], warm_stats: dict[str, float]) -> dict[str, float]: + return { + "first_query_latency_ratio": self._safe_ratio( + cold_stats["first_query_latency"], + warm_stats["first_query_latency"], + ), + "p99_latency_ratio": self._safe_ratio(cold_stats["p99_latency"], warm_stats["p99_latency"]), + "p95_latency_ratio": self._safe_ratio(cold_stats["p95_latency"], warm_stats["p95_latency"]), + "avg_latency_ratio": self._safe_ratio(cold_stats["avg_latency"], warm_stats["avg_latency"]), + } + + def _run_pass(self, pass_name: str) -> dict[str, float]: + latencies = [] + for emb in self.test_data: + start = time.perf_counter() + self._get_db_search_res(emb) + latencies.append(time.perf_counter() - start) + + if len(latencies) % 100 == 0: + log.debug(f"{pass_name} search_count={len(latencies):3}, latest_latency={latencies[-1]}") + + stats = self._latency_stats(latencies) + log.info( + f"{pass_name} search pass: " + f"queries={len(latencies)}, " + f"first_query_latency={stats['first_query_latency']}, " + f"avg_latency={stats['avg_latency']}, " + f"p99={stats['p99_latency']}, " + f"p95={stats['p95_latency']}" + ) + return stats + + def run(self) -> dict[str, dict[str, float]]: + with self.db.init(): + self.db.prepare_filter(self.filters) + cold_stats = self._run_pass("cold") + warm_stats = self._run_pass("warm") + + return { + "cold_stats": cold_stats, + "warm_stats": warm_stats, + "cold_warm_ratio": self._ratio_stats(cold_stats, warm_stats), + } diff --git a/vectordb_bench/backend/runner/concurrent_runner.py b/vectordb_bench/backend/runner/concurrent_runner.py new file mode 100644 index 000000000..e41af22ee --- /dev/null +++ b/vectordb_bench/backend/runner/concurrent_runner.py @@ -0,0 +1,324 @@ +"""Concurrent insert runner with configurable executor backend. + +Replaces SerialInsertRunner for faster data loading in performance cases. + +Auto-detects thread-unsafe DBs via VectorDB.thread_safe and +falls back to single-worker mode. +""" + +from __future__ import annotations + +import concurrent.futures +import logging +import multiprocessing as mp +import threading +import time +from enum import StrEnum +from typing import TYPE_CHECKING + +import numpy as np + +from vectordb_bench.backend.filter import Filter, FilterOp, non_filter +from vectordb_bench.backend.utils import kill_proc_tree, time_it, timeout +from vectordb_bench.backend.workload import WorkloadKind + +from ... import config +from ...models import LoadTimeoutError, PerformanceTimeoutError +from .executor import AsyncExecutor, ThreadExecutor + +if TYPE_CHECKING: + from vectordb_bench.backend.clients import api + from vectordb_bench.backend.dataset import DatasetManager, FtsDatasetManager + + from .executor import TaskExecutor + +log = logging.getLogger(__name__) + + +class ExecutorBackend(StrEnum): + THREADING = "threading" + ASYNC = "async" + + +class ConcurrentInsertRunner: + """Concurrent insert runner with pluggable executor backend. + + Thread-safety: If db.thread_safe is False, max_workers is clamped to 1 + so the single worker thread uses self.db directly (no deepcopy needed). + + Args: + db: VectorDB instance. + dataset: DatasetManager for batch iteration. + normalize: Whether to L2-normalize embeddings. + filters: Filter configuration. + timeout: Timeout in seconds for the overall operation. + max_workers: Number of concurrent workers (default: min(cpu_count, 4)). + backend: Executor backend to use ('threading' or 'async'). + """ + + def __init__( + self, + db: api.VectorDB, + dataset: DatasetManager, + normalize: bool, + filters: Filter = non_filter, + timeout: float | None = None, + max_workers: int | None = None, + backend: ExecutorBackend = ExecutorBackend.THREADING, + batch_size: int = config.NUM_PER_BATCH, + duration: float | None = None, + with_scalar_labels: bool = False, + tenant_case=None, # noqa: ANN001 + workload_kind: WorkloadKind = WorkloadKind.VECTOR, + ): + self.timeout = timeout if isinstance(timeout, int | float) else None + self.dataset: DatasetManager | FtsDatasetManager = dataset + self.db = db + self.normalize = normalize + self.filters = filters + self.backend = backend + self.batch_size = batch_size + self.duration = duration if isinstance(duration, int | float) else None + self.with_scalar_labels = with_scalar_labels + self.tenant_case = tenant_case + self.workload_kind = workload_kind + self._prefetched_fts_batch = None + + effective_workers = max_workers or min(mp.cpu_count(), 4) + if not db.thread_safe: + log.info(f"DB {db.name} is not thread-safe, falling back to max_workers=1") + effective_workers = 1 + self.max_workers = effective_workers + assert db.thread_safe or self.max_workers == 1, ( + "Non-thread-safe DBs must use max_workers=1 — " + "_get_thread_db() relies on this to avoid concurrent access to self.db" + ) + + def __getstate__(self): + """Exclude unpicklable thread-local state for ProcessPoolExecutor(spawn).""" + state = self.__dict__.copy() + state.pop("_iter_lock", None) + state.pop("_dataset_iter", None) + state.pop("_stop_event", None) + return state + + def _create_executor(self) -> TaskExecutor: + if self.backend == ExecutorBackend.ASYNC: + return AsyncExecutor(max_workers=self.max_workers) + return ThreadExecutor(max_workers=self.max_workers) + + def _get_thread_db(self) -> api.VectorDB: + """Return self.db. + + All workers share the connection opened by task()'s `with self.db.init()`. + Thread-safe DBs share it across multiple workers. Non-thread-safe DBs are + clamped to max_workers=1, so there is never concurrent access. + """ + return self.db + + def _insert_batch_with_retry( + self, + db: api.VectorDB, + retry_idx: int = 0, + **insert_kwargs, + ) -> int: + """Insert a single batch with retry logic. Returns inserted count.""" + if getattr(self, "workload_kind", WorkloadKind.VECTOR) == WorkloadKind.FULL_TEXT: + insert_count, error = db.insert_documents(**insert_kwargs) + else: + insert_count, error = db.insert_embeddings(**insert_kwargs) + if error is not None: + log.warning(f"Insert failed, try_idx={retry_idx}, Exception: {error}") + if getattr(error, "non_retryable", False): + msg = f"Non-retryable insert failure after {insert_count} inserted rows: {error}" + raise RuntimeError(msg) from error + retry_idx += 1 + if retry_idx <= config.MAX_INSERT_RETRY: + time.sleep(retry_idx) + return self._insert_batch_with_retry( + db, + retry_idx, + **insert_kwargs, + ) + msg = f"Insert failed and retried more than {config.MAX_INSERT_RETRY} times" + raise RuntimeError(msg) + return insert_count + + def _worker_insert(self, **insert_kwargs) -> int: + """Worker function: insert a batch with retry.""" + db = self._get_thread_db() + return self._insert_batch_with_retry(db, **insert_kwargs) + + def _next_batch(self) -> dict | None: + if getattr(self, "workload_kind", WorkloadKind.VECTOR) == WorkloadKind.FULL_TEXT: + return self._next_fts_batch() + return self._next_vector_batch() + + def _next_vector_batch(self) -> dict | None: + """Pull the next batch from the shared dataset iterator. + + Thread-safe: only one thread reads from the iterator at a time. + Returns None when the iterator is exhausted. + """ + stop_event = getattr(self, "_stop_event", None) + if stop_event is not None and stop_event.is_set(): + return None + if self._deadline is not None and time.perf_counter() >= self._deadline: + return None + with self._iter_lock: + stop_event = getattr(self, "_stop_event", None) + if stop_event is not None and stop_event.is_set(): + return None + try: + data_df = next(self._dataset_iter) + except StopIteration: + return None + + all_metadata = data_df[self.dataset.data.train_id_field].tolist() + emb_np = np.stack(data_df[self.dataset.data.train_vector_field]) + if self.normalize: + all_embeddings = (emb_np / np.linalg.norm(emb_np, axis=1)[:, np.newaxis]).tolist() + else: + all_embeddings = emb_np.tolist() + del emb_np + + labels_data = None + if self.filters.type == FilterOp.StrEqual or self.with_scalar_labels: + label_field = self.filters.label_field if self.filters.type == FilterOp.StrEqual else "labels" + if self.dataset.data.scalar_labels_file_separated: + labels_data = self.dataset.scalar_labels[label_field][all_metadata].to_list() + else: + labels_data = data_df[label_field].tolist() + + tenant_labels_data = None + if self.tenant_case is not None and getattr(self.tenant_case, "is_multitenant", False): + tenant_labels_data = self.tenant_case.tenant_labels_for_ids(all_metadata) + + insert_kwargs = { + "embeddings": all_embeddings, + "metadata": all_metadata, + "labels_data": labels_data, + } + if tenant_labels_data is not None: + insert_kwargs["tenant_labels_data"] = tenant_labels_data + return insert_kwargs + + def _next_fts_batch(self) -> dict | None: + stop_event = getattr(self, "_stop_event", None) + if stop_event is not None and stop_event.is_set(): + return None + if self._deadline is not None and time.perf_counter() >= self._deadline: + return None + with self._iter_lock: + stop_event = getattr(self, "_stop_event", None) + if stop_event is not None and stop_event.is_set(): + return None + batch = self._prefetched_fts_batch + if batch is not None: + self._prefetched_fts_batch = None + else: + try: + batch = next(self._dataset_iter) + except StopIteration: + return None + + doc_ids = [] + texts = [] + filter_ids = [] + for doc in batch: + doc_ids.append(doc.doc_id if hasattr(doc, "doc_id") else str(doc["doc_id"])) + texts.append(doc.text if hasattr(doc, "text") else doc["text"]) + filter_id = doc.filter_id if hasattr(doc, "filter_id") else doc.get("filter_id", None) + filter_ids.append(filter_id) + insert_kwargs = {"texts": texts, "doc_ids": doc_ids} + if any(filter_id is not None for filter_id in filter_ids): + insert_kwargs["filter_ids"] = filter_ids + return insert_kwargs + + def _worker_loop(self) -> int: + """Worker loop: pull batches from the shared iterator and insert them.""" + total = 0 + try: + while True: + batch = self._next_batch() + if batch is None: + break + total += self._worker_insert(**batch) + except Exception: + stop_event = getattr(self, "_stop_event", None) + if stop_event is not None: + stop_event.set() + raise + return total + + def task(self) -> int: + """Insert entire dataset using concurrent executor. Runs in subprocess.""" + count = 0 + self._iter_lock = threading.Lock() + self._stop_event = threading.Event() + self._deadline = None if self.duration is None else time.perf_counter() + self.duration + self._dataset_iter = self.dataset.iter_batches(self.batch_size) + if getattr(self, "workload_kind", WorkloadKind.VECTOR) == WorkloadKind.FULL_TEXT: + # Prime lazy ir_datasets document preparation before timed backend inserts. + self._prefetched_fts_batch = next(self._dataset_iter, None) + + with self.db.init(): + log.info( + f"({mp.current_process().name:16}) Start concurrent insert, " + f"batch_size={self.batch_size}, max_workers={self.max_workers}, workload={self.workload_kind}" + ) + start = time.perf_counter() + + with self._create_executor() as executor: + for _ in range(self.max_workers): + executor.submit(self._worker_loop) + + batch_results = executor.wait_all() + + # Log all errors, then raise the first one + errors = [r.error for r in batch_results if r.error is not None] + if errors: + for err in errors: + log.warning(f"Batch insert error: {err}") + raise errors[0] + + count = sum(r.value for r in batch_results) + + log.info( + f"({mp.current_process().name:16}) Finish concurrent insert, " + f"count={count}, dur={time.perf_counter() - start:.2f}s" + ) + return count + + @time_it + def _insert_all_batches(self) -> int: + """Performance case only: run task() in subprocess with timeout.""" + if getattr(self, "workload_kind", WorkloadKind.VECTOR) == WorkloadKind.FULL_TEXT: + # FTS datasets come from ir_datasets, whose loaded objects may contain + # lambdas or handles that cannot be pickled by ProcessPoolExecutor. + # Keep FTS loading in this process and use threads to overlap the + # I/O-bound insert RPCs instead of Python CPU-bound work. + with timeout(self.timeout, lambda: LoadTimeoutError(self.timeout)): + return self.task() + with concurrent.futures.ProcessPoolExecutor( + mp_context=mp.get_context("spawn"), + max_workers=1, + ) as executor: + future = executor.submit(self.task) + try: + count = future.result(timeout=self.timeout) + except TimeoutError as e: + msg = f"VectorDB load dataset timeout in {self.timeout}" + log.warning(msg) + kill_proc_tree(pids=list(executor._processes.keys())) + raise PerformanceTimeoutError(msg) from e + except Exception as e: + log.warning(f"VectorDB load dataset error: {e}") + raise e from e + else: + return count + + def run(self) -> int: + """Insert full dataset concurrently. Returns total inserted count.""" + count, _ = self._insert_all_batches() + return count diff --git a/vectordb_bench/backend/runner/executor.py b/vectordb_bench/backend/runner/executor.py new file mode 100644 index 000000000..0bff2dc2a --- /dev/null +++ b/vectordb_bench/backend/runner/executor.py @@ -0,0 +1,170 @@ +"""Task executor abstraction with threading and async backends. + +Provides a unified interface for submitting callables with controlled +concurrency. Two implementations: + - ThreadExecutor: backed by ThreadPoolExecutor + - AsyncExecutor: backed by asyncio with semaphore-based concurrency control +""" + +from __future__ import annotations + +import asyncio +import logging +from abc import ABC, abstractmethod +from concurrent.futures import Future, ThreadPoolExecutor +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from collections.abc import Callable + +log = logging.getLogger(__name__) + + +@dataclass +class TaskResult: + """Result of a single submitted task.""" + + value: Any = None + error: Exception | None = None + + @property + def success(self) -> bool: + return self.error is None + + +class TaskExecutor(ABC): + """Abstract executor that accepts callables and controls concurrency.""" + + @abstractmethod + def start(self) -> None: + """Initialize executor resources.""" + raise NotImplementedError + + @abstractmethod + def submit(self, fn: Callable[..., Any], *args: Any, **kwargs: Any) -> None: + """Submit a task for execution.""" + raise NotImplementedError + + @abstractmethod + def wait_all(self) -> list[TaskResult]: + """Block until all submitted tasks complete. Return results in submission order.""" + raise NotImplementedError + + @abstractmethod + def shutdown(self) -> None: + """Release executor resources. Safe to call multiple times.""" + raise NotImplementedError + + def __enter__(self): + self.start() + return self + + def __exit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: object) -> bool: + self.shutdown() + return False + + +class ThreadExecutor(TaskExecutor): + """ThreadPoolExecutor-backed implementation.""" + + def __init__(self, max_workers: int): + self._max_workers = max(1, max_workers) + self._executor: ThreadPoolExecutor | None = None + self._futures: list[Future] = [] + + def start(self) -> None: + self._executor = ThreadPoolExecutor(max_workers=self._max_workers) + self._futures = [] + + def submit(self, fn: Callable[..., Any], *args: Any, **kwargs: Any) -> None: + if self._executor is None: + raise RuntimeError("Executor not started. Call start() or use as context manager.") + future = self._executor.submit(fn, *args, **kwargs) + self._futures.append(future) + + def wait_all(self) -> list[TaskResult]: + results = [] + for future in self._futures: + try: + value = future.result() + results.append(TaskResult(value=value)) + except Exception as e: + results.append(TaskResult(error=e)) + self._futures = [] + return results + + def shutdown(self) -> None: + if self._executor is not None: + self._executor.shutdown(wait=True) + self._executor = None + + +class AsyncExecutor(TaskExecutor): + """asyncio-backed implementation for async DB clients. + + Accepts coroutine functions (async def), runs them on a single event + loop thread with semaphore-based concurrency control. No thread pool. + """ + + def __init__(self, max_workers: int): + self._max_workers = max(1, max_workers) + self._loop: asyncio.AbstractEventLoop | None = None + self._semaphore: asyncio.Semaphore | None = None + self._coros: list = [] + self._owns_loop = False + + def start(self) -> None: + try: + self._loop = asyncio.get_running_loop() + except RuntimeError: + self._loop = asyncio.new_event_loop() + self._owns_loop = True + self._semaphore = asyncio.Semaphore(self._max_workers) + self._coros = [] + + def submit(self, fn: Callable[..., Any], *args: Any, **kwargs: Any) -> None: + """Submit a callable for execution. + + Accepts both coroutine functions (async def) and regular functions. + Sync functions are offloaded to a thread via run_in_executor. + """ + if self._loop is None or self._semaphore is None: + raise RuntimeError("Executor not started. Call start() or use as context manager.") + + async def _run(): + async with self._semaphore: + if asyncio.iscoroutinefunction(fn): + return await fn(*args, **kwargs) + loop = asyncio.get_running_loop() + return await loop.run_in_executor(None, lambda: fn(*args, **kwargs)) + + self._coros.append(_run()) + + def wait_all(self) -> list[TaskResult]: + if not self._coros: + return [] + + async def _gather(): + gathered = await asyncio.gather(*self._coros, return_exceptions=True) + results = [] + for item in gathered: + if isinstance(item, Exception): + results.append(TaskResult(error=item)) + else: + results.append(TaskResult(value=item)) + return results + + if self._owns_loop: + results = self._loop.run_until_complete(_gather()) + else: + results = asyncio.run_coroutine_threadsafe(_gather(), self._loop).result() + + self._coros = [] + return results + + def shutdown(self) -> None: + if self._owns_loop and self._loop is not None: + self._loop.close() + self._loop = None + self._semaphore = None diff --git a/vectordb_bench/backend/runner/mp_runner.py b/vectordb_bench/backend/runner/mp_runner.py index b7823af37..bf81d2a7e 100644 --- a/vectordb_bench/backend/runner/mp_runner.py +++ b/vectordb_bench/backend/runner/mp_runner.py @@ -12,6 +12,8 @@ from hdrh.histogram import HdrHistogram from vectordb_bench.backend.filter import Filter, non_filter +from vectordb_bench.backend.payload import PayloadProfile +from vectordb_bench.backend.workload import WorkloadKind from ... import config from ...models import ConcurrencySlotTimeoutError @@ -45,10 +47,31 @@ def __init__( concurrencies: Iterable[int] = config.NUM_CONCURRENCY, duration: int = config.CONCURRENCY_DURATION, concurrency_timeout: int = config.CONCURRENCY_TIMEOUT, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + tenant_labels: list[str] | None = None, + workload_kind: WorkloadKind = WorkloadKind.VECTOR, ): self.db = db self.k = k self.filters = filters + self.workload_kind = workload_kind + self.payload_profile = payload_profile + self.tenant_labels = tenant_labels or [] + if self.workload_kind == WorkloadKind.FULL_TEXT: + self._search_func = self.db.search_documents + elif self.workload_kind == WorkloadKind.VECTOR: + self._search_func = self._search_embedding + else: + msg = f"Unsupported search workload: {workload_kind}" + raise NotImplementedError(msg) + if self.workload_kind == WorkloadKind.VECTOR and not self.db.supports_payload_profile(self.payload_profile): + msg = f"{self.db.name} does not support payload_profile={self.payload_profile.value}" + raise NotImplementedError(msg) + if self.workload_kind == WorkloadKind.FULL_TEXT and not self.db.supports_document_payload_profile( + self.payload_profile + ): + msg = f"{self.db.name} does not support document payload_profile={self.payload_profile.value}" + raise NotImplementedError(msg) self.concurrencies = concurrencies self.duration = duration self.concurrency_timeout = concurrency_timeout @@ -56,6 +79,42 @@ def __init__( self.test_data = test_data log.debug(f"test dataset columns: {len(test_data)}") + def __getstate__(self): + state = self.__dict__.copy() + state.pop("_search_func", None) + return state + + def __setstate__(self, state: dict): + self.__dict__.update(state) + if self.workload_kind == WorkloadKind.FULL_TEXT: + self._search_func = self.db.search_documents + elif self.workload_kind == WorkloadKind.VECTOR: + self._search_func = self._search_embedding + else: + msg = f"Unsupported search workload: {self.workload_kind}" + raise NotImplementedError(msg) + + def _search_embedding(self, emb: list[float], tenant: str | None = None) -> list[int]: + if tenant is None: + if self.payload_profile == PayloadProfile.IDS_ONLY: + return self.db.search_embedding(emb, self.k) + return self.db.search_embedding(emb, self.k, payload_profile=self.payload_profile) + if self.payload_profile == PayloadProfile.IDS_ONLY: + return self.db.search_embedding(emb, self.k, tenant=tenant) + return self.db.search_embedding(emb, self.k, payload_profile=self.payload_profile, tenant=tenant) + + def _search_once(self, query: list[float] | str, tenant_rng: random.Random | None = None): + if self.workload_kind == WorkloadKind.FULL_TEXT: + if self.payload_profile == PayloadProfile.IDS_ONLY: + return self._search_func(query, self.k) + return self._search_func(query, self.k, payload_profile=self.payload_profile) + tenant = ( + self.tenant_labels[tenant_rng.randrange(len(self.tenant_labels))] + if tenant_rng is not None and self.tenant_labels + else None + ) + return self._search_func(query, tenant=tenant) + def search( self, test_data: list[list[float]], @@ -75,6 +134,7 @@ def search( with self.db.init(): self.db.prepare_filter(self.filters) num, idx = len(test_data), random.randint(0, len(test_data) - 1) + tenant_rng = random.Random(mp.current_process().pid or 0) start_time = time.perf_counter() count = 0 @@ -82,7 +142,7 @@ def search( while time.perf_counter() < start_time + self.duration: s = time.perf_counter() try: - self.db.search_embedding(test_data[idx], self.k) + self._search_once(test_data[idx], tenant_rng=tenant_rng) count += 1 latencies.append(time.perf_counter() - s) except Exception as e: @@ -341,7 +401,7 @@ def search_by_dur( while time.perf_counter() < start_time + dur: s = time.perf_counter() try: - self.db.search_embedding(test_data[idx], self.k) + self._search_once(test_data[idx]) success_count += 1 latency_us = int((time.perf_counter() - s) * US_TO_SECONDS) histogram.record_value(min(latency_us, HDR_HISTOGRAM_MAX_US)) diff --git a/vectordb_bench/backend/runner/rate_runner.py b/vectordb_bench/backend/runner/rate_runner.py index c56c8ca61..91d0bb3ee 100644 --- a/vectordb_bench/backend/runner/rate_runner.py +++ b/vectordb_bench/backend/runner/rate_runner.py @@ -3,7 +3,7 @@ import multiprocessing as mp import time from concurrent.futures import ThreadPoolExecutor -from copy import deepcopy +from copy import copy, deepcopy from vectordb_bench import config from vectordb_bench.backend.clients import api @@ -65,6 +65,29 @@ def _insert_embeddings(db: api.VectorDB, emb: list[list[float]], metadata: list[ log.debug("Failed to reset Doris client or table on thread-local copy", exc_info=True) with db_copy.init(): _insert_embeddings(db_copy, emb, metadata, retry_idx=0) + elif db.name == "SeekDB": + # mysql.connector is not thread-safe; do not share one connection across workers. + # deepcopy() fails on an open _conn (socket is not picklable / not copy-safe in spawn workers). + db_copy = copy(db) + try: + db_copy._conn = None + db_copy._cursor = None + except Exception: + log.debug("Failed to reset SeekDB connection on thread-local copy", exc_info=True) + with db_copy.init(): + _insert_embeddings(db_copy, emb, metadata, retry_idx=0) + elif db.name == "VolcMySQL": + # mysql.connector is not thread-safe; do not share one connection across workers. + # deepcopy() fails on an open conn (socket is not picklable / not copy-safe in spawn workers). + db_copy = copy(db) + try: + db_copy.conn = None + db_copy.cursor = None + db_copy.admin_cursor = None + except Exception: + log.debug("Failed to reset VolcMySQL connection on thread-local copy", exc_info=True) + with db_copy.init(): + _insert_embeddings(db_copy, emb, metadata, retry_idx=0) else: _insert_embeddings(db, emb, metadata, retry_idx=0) diff --git a/vectordb_bench/backend/runner/serial_runner.py b/vectordb_bench/backend/runner/serial_runner.py index db3595c49..f671d381a 100644 --- a/vectordb_bench/backend/runner/serial_runner.py +++ b/vectordb_bench/backend/runner/serial_runner.py @@ -2,18 +2,20 @@ import logging import math import multiprocessing as mp +import random import time import traceback import numpy as np -import psutil from vectordb_bench.backend.dataset import DatasetManager -from vectordb_bench.backend.filter import Filter, FilterOp, non_filter +from vectordb_bench.backend.filter import Filter, non_filter +from vectordb_bench.backend.payload import PayloadProfile +from vectordb_bench.backend.workload import WorkloadKind from ... import config -from ...metric import calc_ndcg, calc_recall, get_ideal_dcg -from ...models import LoadTimeoutError, PerformanceTimeoutError +from ...metric import calc_mrr_fts, calc_ndcg, calc_ndcg_fts, calc_recall, calc_recall_fts, get_ideal_dcg +from ...models import LoadTimeoutError from .. import utils from ..clients import api @@ -24,6 +26,9 @@ class SerialInsertRunner: + # FTS insert is intentionally not implemented here. FTS performance loading + # goes through ConcurrentInsertRunner; serial FTS insert can be added later + # if a capacity or serial-load FTS case needs it. def __init__( self, db: api.VectorDB, @@ -38,66 +43,6 @@ def __init__( self.normalize = normalize self.filters = filters - def retry_insert(self, db: api.VectorDB, retry_idx: int = 0, **kwargs): - _, error = db.insert_embeddings(**kwargs) - if error is not None: - log.warning(f"Insert Failed, try_idx={retry_idx}, Exception: {error}") - retry_idx += 1 - if retry_idx <= config.MAX_INSERT_RETRY: - time.sleep(retry_idx) - self.retry_insert(db, retry_idx=retry_idx, **kwargs) - else: - msg = f"Insert failed and retried more than {config.MAX_INSERT_RETRY} times" - raise RuntimeError(msg) from None - - def task(self) -> int: - count = 0 - with self.db.init(): - log.info(f"({mp.current_process().name:16}) Start inserting embeddings in batch {config.NUM_PER_BATCH}") - start = time.perf_counter() - for data_df in self.dataset: - all_metadata = data_df[self.dataset.data.train_id_field].tolist() - - emb_np = np.stack(data_df[self.dataset.data.train_vector_field]) - if self.normalize: - log.debug("normalize the 100k train data") - all_embeddings = (emb_np / np.linalg.norm(emb_np, axis=1)[:, np.newaxis]).tolist() - else: - all_embeddings = emb_np.tolist() - del emb_np - log.debug(f"batch dataset size: {len(all_embeddings)}, {len(all_metadata)}") - - labels_data = None - if self.filters.type == FilterOp.StrEqual: - if self.dataset.data.scalar_labels_file_separated: - labels_data = self.dataset.scalar_labels[self.filters.label_field][all_metadata].to_list() - else: - labels_data = data_df[self.filters.label_field].tolist() - - insert_count, error = self.db.insert_embeddings( - embeddings=all_embeddings, - metadata=all_metadata, - labels_data=labels_data, - ) - if error is not None: - self.retry_insert( - self.db, - embeddings=all_embeddings, - metadata=all_metadata, - labels_data=labels_data, - ) - - assert insert_count == len(all_metadata) - count += insert_count - if count % 100_000 == 0: - log.info(f"({mp.current_process().name:16}) Loaded {count} embeddings into VectorDB") - - log.info( - f"({mp.current_process().name:16}) Finish loading all dataset into VectorDB, " - f"dur={time.perf_counter() - start}" - ) - return count - def endless_insert_data(self, all_embeddings: list, all_metadata: list, left_id: int = 0) -> int: with self.db.init(): # unique id for endlessness insertion @@ -147,45 +92,6 @@ def endless_insert_data(self, all_embeddings: list, all_metadata: list, left_id: ) return count - @utils.time_it - def _insert_all_batches(self) -> int: - """Performance case only""" - - def stop_worker_processes(): - processes = list((executor._processes or {}).keys()) - executor.shutdown(wait=False, cancel_futures=True) - for pid in processes: - try: - proc = psutil.Process(pid) - proc.terminate() - proc.wait(timeout=5) - except psutil.NoSuchProcess: - continue - except psutil.TimeoutExpired: - psutil.Process(pid).kill() - - executor = concurrent.futures.ProcessPoolExecutor( - mp_context=mp.get_context("spawn"), - max_workers=1, - ) - try: - future = executor.submit(self.task) - try: - count = future.result(timeout=self.timeout) - except TimeoutError as e: - msg = f"VectorDB load dataset timeout in {self.timeout}" - log.warning(msg) - for pid, _ in executor._processes.items(): - psutil.Process(pid).kill() - raise PerformanceTimeoutError(msg) from e - except Exception as e: - log.warning(f"VectorDB load dataset error: {e}") - raise e from e - else: - return count - finally: - stop_worker_processes() - def run_endlessness(self) -> int: """run forever util DB raises exception or crash""" # datasets for load tests are quite small, can fit into memory @@ -221,23 +127,44 @@ def run_endlessness(self) -> int: else: raise LoadTimeoutError(self.timeout) - def run(self) -> int: - count, _ = self._insert_all_batches() - return count - class SerialSearchRunner: def __init__( self, db: api.VectorDB, - test_data: list[list[float]], - ground_truth: list[list[int]], + test_data: list, + ground_truth: list[list[int]] | list[dict[str, int]], k: int = 100, filters: Filter = non_filter, + payload_profile: PayloadProfile = PayloadProfile.IDS_ONLY, + tenant_labels: list[str] | None = None, + measure_recall: bool = True, + workload_kind: WorkloadKind = WorkloadKind.VECTOR, ): self.db = db self.k = k self.filters = filters + self.workload_kind = workload_kind + self.payload_profile = payload_profile + self.tenant_labels = tenant_labels or [] + self.measure_recall = measure_recall + if workload_kind == WorkloadKind.FULL_TEXT: + self._search_func = self.db.search_documents + self._use_fts_metrics = True + elif workload_kind == WorkloadKind.VECTOR: + self._search_func = self._search_embedding + self._use_fts_metrics = False + else: + msg = f"Unsupported search workload: {workload_kind}" + raise NotImplementedError(msg) + if self.workload_kind == WorkloadKind.VECTOR and not self.db.supports_payload_profile(self.payload_profile): + msg = f"{self.db.name} does not support payload_profile={self.payload_profile.value}" + raise NotImplementedError(msg) + if self.workload_kind == WorkloadKind.FULL_TEXT and not self.db.supports_document_payload_profile( + self.payload_profile + ): + msg = f"{self.db.name} does not support document payload_profile={self.payload_profile.value}" + raise NotImplementedError(msg) if isinstance(test_data[0], np.ndarray): self.test_data = [query.tolist() for query in test_data] @@ -245,47 +172,80 @@ def __init__( self.test_data = test_data self.ground_truth = ground_truth - def _get_db_search_res(self, emb: list[float], retry_idx: int = 0) -> list[int]: + def _search_embedding(self, emb: list[float], tenant: str | None = None) -> list[int]: + if tenant is None: + if self.payload_profile == PayloadProfile.IDS_ONLY: + return self.db.search_embedding(emb, self.k) + return self.db.search_embedding(emb, self.k, payload_profile=self.payload_profile) + if self.payload_profile == PayloadProfile.IDS_ONLY: + return self.db.search_embedding(emb, self.k, tenant=tenant) + return self.db.search_embedding(emb, self.k, payload_profile=self.payload_profile, tenant=tenant) + + def _get_db_search_res( + self, + query: list[float] | str, + tenant: str | None = None, + retry_idx: int = 0, + ) -> list[int]: try: - results = self.db.search_embedding(emb, self.k) + if self.workload_kind == WorkloadKind.FULL_TEXT: + if self.payload_profile == PayloadProfile.IDS_ONLY: + results = self._search_func(query, self.k) + else: + results = self._search_func(query, self.k, payload_profile=self.payload_profile) + else: + results = self._search_func(query, tenant=tenant) except Exception as e: log.warning(f"Serial search failed, retry_idx={retry_idx}, Exception: {e}") if retry_idx < config.MAX_SEARCH_RETRY: - return self._get_db_search_res(emb=emb, retry_idx=retry_idx + 1) + return self._get_db_search_res(query=query, tenant=tenant, retry_idx=retry_idx + 1) msg = f"Serial search failed and retried more than {config.MAX_SEARCH_RETRY} times" raise RuntimeError(msg) from e return results - def search(self, args: tuple[list, list[list[int]]]) -> tuple[float, float, float, float]: + def search(self, args: tuple[list, list[list[int]] | list[dict[str, int]]]) -> tuple[float, ...]: log.info(f"{mp.current_process().name:14} start search the entire test_data to get recall and latency") with self.db.init(): self.db.prepare_filter(self.filters) test_data, ground_truth = args - ideal_dcg = get_ideal_dcg(self.k) + ideal_dcg = None if self._use_fts_metrics else get_ideal_dcg(self.k) log.debug(f"test dataset size: {len(test_data)}") - log.debug(f"ground truth size: {len(ground_truth)}") + log.debug(f"ground truth size: {len(ground_truth) if ground_truth is not None else 0}") - latencies, recalls, ndcgs = [], [], [] + latencies, recalls, ndcgs, mrrs = [], [], [], [] + tenant_rng = random.Random(0) for idx, emb in enumerate(test_data): + tenant = ( + self.tenant_labels[tenant_rng.randrange(len(self.tenant_labels))] + if self.workload_kind == WorkloadKind.VECTOR and self.tenant_labels + else None + ) s = time.perf_counter() try: - results = self._get_db_search_res(emb) + results = self._get_db_search_res(emb, tenant=tenant) except Exception as e: log.warning(f"VectorDB search_embedding error: {e}") raise e from None latencies.append(time.perf_counter() - s) - if ground_truth is not None: + if self.measure_recall and ground_truth is not None: gt = ground_truth[idx] - recalls.append(calc_recall(self.k, gt[: self.k], results)) - ndcgs.append(calc_ndcg(gt[: self.k], results, ideal_dcg)) + if self._use_fts_metrics: + recalls.append(calc_recall_fts(self.k, gt, results)) + ndcgs.append(calc_ndcg_fts(self.k, gt, results)) + mrrs.append(calc_mrr_fts(self.k, gt, results)) + else: + recalls.append(calc_recall(self.k, gt[: self.k], results)) + ndcgs.append(calc_ndcg(gt[: self.k], results, ideal_dcg)) else: recalls.append(0) ndcgs.append(0) + if self._use_fts_metrics: + mrrs.append(0) if len(latencies) % 100 == 0: log.debug( @@ -299,6 +259,21 @@ def search(self, args: tuple[list, list[list[int]]]) -> tuple[float, float, floa cost = round(np.sum(latencies), 4) p99 = round(np.percentile(latencies, 99), 4) p95 = round(np.percentile(latencies, 95), 4) + if self._use_fts_metrics: + avg_mrr = round(np.mean(mrrs), 4) + log.info( + f"{mp.current_process().name:14} search entire test_data: " + f"cost={cost}s, " + f"queries={len(latencies)}, " + f"avg_recall={avg_recall}, " + f"avg_ndcg={avg_ndcg}, " + f"avg_mrr={avg_mrr}, " + f"avg_latency={avg_latency}, " + f"p99={p99}, " + f"p95={p95}" + ) + return (avg_recall, avg_ndcg, avg_mrr, p99, p95) + log.info( f"{mp.current_process().name:14} search entire test_data: " f"cost={cost}s, " @@ -311,13 +286,13 @@ def search(self, args: tuple[list, list[list[int]]]) -> tuple[float, float, floa ) return (avg_recall, avg_ndcg, p99, p95) - def _run_in_subprocess(self) -> tuple[float, float, float, float]: + def _run_in_subprocess(self) -> tuple[float, ...]: with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor: future = executor.submit(self.search, (self.test_data, self.ground_truth)) return future.result() @utils.time_it - def run(self) -> tuple[float, float, float, float]: + def run(self) -> tuple[float, ...]: log.info(f"{mp.current_process().name:14} start serial search") if self.test_data is None: msg = "empty test_data" @@ -326,11 +301,11 @@ def run(self) -> tuple[float, float, float, float]: return self._run_in_subprocess() @utils.time_it - def run_with_cost(self) -> tuple[tuple[float, float, float, float], float]: + def run_with_cost(self) -> tuple[tuple[float, ...], float]: """ Search all test data in serial. Returns: - tuple[tuple[float, float, float, float], float]: (avg_recall, avg_ndcg, p99_latency, p95_latency), cost + tuple[tuple[float, ...], float]: search metrics and cost """ log.info(f"{mp.current_process().name:14} start serial search") if self.test_data is None: diff --git a/vectordb_bench/backend/task_runner.py b/vectordb_bench/backend/task_runner.py index 89a70d5fb..d66d6f454 100644 --- a/vectordb_bench/backend/task_runner.py +++ b/vectordb_bench/backend/task_runner.py @@ -2,12 +2,13 @@ import hashlib import logging import re +import time import traceback from enum import Enum, auto import numpy as np -import psutil +from .. import config from ..base import BaseModel from ..metric import Metric from ..models import PerformanceTimeoutError, TaskConfig, TaskStage @@ -15,7 +16,16 @@ from .cases import Case, CaseLabel, StreamingPerformanceCase from .clients import DB, MetricType, api from .data_source import DatasetSource -from .runner import MultiProcessingSearchRunner, ReadWriteRunner, SerialInsertRunner, SerialSearchRunner +from .runner import ( + ColdWarmSearchRunner, + ConcurrentInsertRunner, + MultiProcessingSearchRunner, + ReadWriteRunner, + SerialInsertRunner, + SerialSearchRunner, +) +from .utils import kill_proc_tree +from .workload import WorkloadKind log = logging.getLogger(__name__) @@ -46,46 +56,107 @@ class CaseRunner(BaseModel): db: api.VectorDB | None = None test_emb: list[list[float]] | None = None + test_texts: list[str] | None = None serial_search_runner: SerialSearchRunner | None = None search_runner: MultiProcessingSearchRunner | None = None final_search_runner: MultiProcessingSearchRunner | None = None read_write_runner: ReadWriteRunner | None = None + cold_warm_search_runner: ColdWarmSearchRunner | None = None def __eq__(self, obj: any): if isinstance(obj, CaseRunner): - return ( - self.ca.label == CaseLabel.Performance - and self.config.db == obj.config.db - and self.config.db_case_config == obj.config.db_case_config - and self.ca.dataset == obj.ca.dataset - ) + key = self.load_reuse_key() + return key is not None and key == obj.load_reuse_key() return False def __hash__(self) -> int: """Hash method to maintain consistency with __eq__ method.""" - return hash( - ( - self.ca.label, - self.config.db, - self.config.db_case_config, - self.ca.dataset, - ) + return hash(self.load_reuse_key()) + + def load_reuse_key(self) -> tuple | None: + if self.ca.label != CaseLabel.Performance: + return None + return ( + self.config.db.value, + self._db_config_hash_key(), + self._db_case_config_hash_key(), + self._collection_name_hash_key(), + self._dataset_hash_key(), + self.ca.with_scalar_labels, + self.ca.is_multitenant, + self._multitenant_routing_hash_key(), + ) + + @classmethod + def _hashable_value(cls, value: object) -> object: + if isinstance(value, dict): + hashable = tuple(sorted((str(k), cls._hashable_value(v)) for k, v in value.items())) + elif isinstance(value, (list, tuple)): + hashable = tuple(cls._hashable_value(v) for v in value) + elif isinstance(value, (set, frozenset)): + hashable = tuple(sorted((cls._hashable_value(v) for v in value), key=repr)) + elif isinstance(value, Enum): + hashable = value.value + elif hasattr(value, "model_dump"): + hashable = cls._hashable_value(value.model_dump(mode="json")) + elif hasattr(value, "get_secret_value"): + hashable = value.get_secret_value() + else: + hashable = value + return hashable + + def _db_config_hash_key(self) -> object: + db_config = self.config.db_config + if hasattr(db_config, "to_dict"): + return self._hashable_value(db_config.to_dict()) + return self._hashable_value(db_config) + + def _db_case_config_hash_key(self) -> object: + return self._hashable_value(self.config.db_case_config) + + def _collection_name_hash_key(self) -> str | None: + return self._doris_collection_name() + + def _dataset_hash_key(self) -> object: + return self._hashable_value(self.ca.dataset.data) + + def _multitenant_routing_hash_key(self) -> tuple | None: + if not self.ca.is_multitenant: + return None + return ( + getattr(self.ca, "tenant_count", None), + getattr(self.ca, "tenant_prefix", None), + getattr(self.ca, "tenant_id_width", None), + getattr(self.ca, "tenant_distribution", None), ) + def _doris_collection_name(self) -> str | None: + if self.config.db != DB.Doris: + return None + case_type_name = self.config.case_config.case_id.name + base = f"{case_type_name.lower()}" + base = re.sub(r"[^a-z0-9_]+", "_", base).strip("_") + if len(base) > 63: + h = hashlib.md5(base.encode(), usedforsecurity=False).hexdigest()[:6] + base = f"{base[:(63-7)]}_{h}" + return base + def display(self) -> dict: - c_dict = self.ca.model_dump( + dataset_include = { + "name": True, + "size": True, + "label": True, + "metric_type": True, + } + if self.ca.label != CaseLabel.FullTextSearchPerformance: + dataset_include["dim"] = True + c_dict = self.ca.dict( include={ "label": True, "name": True, "filters": True, "dataset": { - "data": { - "name": True, - "size": True, - "dim": True, - "metric_type": True, - "label": True, - }, + "data": dataset_include, }, }, ) @@ -95,24 +166,26 @@ def display(self) -> dict: @property def normalize(self) -> bool: assert self.db + if self.is_fts: + return False return self.db.need_normalize_cosine() and self.ca.dataset.data.metric_type == MetricType.COSINE + @property + def workload_kind(self) -> WorkloadKind: + if getattr(self.ca, "label", None) == CaseLabel.FullTextSearchPerformance: + return WorkloadKind.FULL_TEXT + return WorkloadKind.VECTOR + + @property + def is_fts(self) -> bool: + return self.workload_kind == WorkloadKind.FULL_TEXT + def init_db(self, drop_old: bool = True) -> None: db_cls = self.config.db.init_cls # Compose a compact, case-unique collection/table name for Doris to avoid cross-case interference collection_name = None try: - if self.config.db == DB.Doris: - # Primary identifier = case-type enum name from CLI (e.g., Performance768D10M) - case_type_name = self.config.case_config.case_id.name - base = f"{case_type_name.lower()}" - # Sanitize to [a-z0-9_] - base = re.sub(r"[^a-z0-9_]+", "_", base).strip("_") - # Cap to 63 chars; add short hash if truncated - if len(base) > 63: - h = hashlib.md5(base.encode(), usedforsecurity=False).hexdigest()[:6] - base = f"{base[:(63-7)]}_{h}" - collection_name = base + collection_name = self._doris_collection_name() except Exception: # If anything goes wrong, fall back silently; Doris will use its default name logic collection_name = None @@ -122,23 +195,75 @@ def init_db(self, drop_old: bool = True) -> None: if "collection_name" in db_config_dict and not collection_name: collection_name = db_config_dict.pop("collection_name") + extra_db_kwargs = {} + if collection_name: + extra_db_kwargs["collection_name"] = collection_name + if self.ca.is_multitenant: + extra_db_kwargs["multitenant_tenant_labels"] = self.ca.tenant_labels() + self.db = db_cls( - dim=self.ca.dataset.data.dim, + dim=getattr(self.ca.dataset.data, "dim", 0), db_config=db_config_dict, db_case_config=self.config.db_case_config, drop_old=drop_old, with_scalar_labels=self.ca.with_scalar_labels, - **({"collection_name": collection_name} if collection_name else {}), + **extra_db_kwargs, ) def _pre_run(self, drop_old: bool = True): try: + self._validate_cloud_cold_latency_config(drop_old) + creates_multitenant_collection = ( + TaskStage.DROP_OLD in self.config.stages or TaskStage.LOAD in self.config.stages + ) + if ( + self.ca.is_multitenant + and self.config.db in {DB.Milvus, DB.ZillizCloud} + and creates_multitenant_collection + and not getattr(self.config.db_case_config, "use_partition_key", False) + ): + msg = "CloudMultiTenantSearchCase requires use_partition_key=True for Milvus/ZillizCloud" + raise ValueError(msg) + + if self.is_fts: + self.ca.dataset.prepare( + self.dataset_source, + filters=self.ca.filters, + ) + self.init_db(drop_old) + return + self.init_db(drop_old) - self.ca.dataset.prepare(self.dataset_source, filters=self.ca.filters) + if self.ca.is_multitenant and self.db is not None: + if not self.db.supports_multitenant(): + msg = f"{self.config.db_name} does not support CloudMultiTenantSearchCase" + raise NotImplementedError(msg) + self.db.set_multitenant_context(self.ca.tenant_labels()) + if self.config.db in {DB.Milvus, DB.ZillizCloud} and not creates_multitenant_collection: + self.db.validate_multitenant_schema() + self.ca.dataset.prepare( + self.dataset_source, + filters=self.ca.filters, + with_train_files=TaskStage.LOAD in self.config.stages, + with_scalar_labels=self.ca.with_scalar_labels, + ) except ModuleNotFoundError as e: log.warning(f"pre run case error: please install client for db: {self.config.db}, error={e}") raise e from None + def _validate_cloud_cold_latency_config(self, drop_old: bool) -> None: + if getattr(self.ca, "label", None) != CaseLabel.CloudColdLatency: + return + if drop_old: + msg = ( + "CloudColdLatencyCase requires an existing cold collection. " + "Run with --skip-drop-old and --skip-load." + ) + raise ValueError(msg) + if TaskStage.LOAD in self.config.stages: + msg = "CloudColdLatencyCase is search-only. Run with --skip-load." + raise ValueError(msg) + def run(self, drop_old: bool = True) -> Metric: log.info("Starting run") @@ -146,10 +271,14 @@ def run(self, drop_old: bool = True) -> Metric: if self.ca.label == CaseLabel.Load: return self._run_capacity_case() - if self.ca.label == CaseLabel.Performance: + if self.ca.label in {CaseLabel.Performance, CaseLabel.FullTextSearchPerformance}: return self._run_perf_case(drop_old) if self.ca.label == CaseLabel.Streaming: return self._run_streaming_case() + if self.ca.label == CaseLabel.CloudInsert: + return self._run_cloud_insert_case() + if self.ca.label == CaseLabel.CloudColdLatency: + return self._run_cloud_cold_latency_case(drop_old) msg = f"unknown case type: {self.ca.label}" log.warning(msg) raise ValueError(msg) @@ -188,13 +317,28 @@ def _run_perf_case(self, drop_old: bool = True) -> Metric: log.info("Start performance case") try: m = Metric() + if self.is_fts and getattr(self.ca.dataset, "filter_stats", None): + m.additional_parameters["fts_filter"] = dict(self.ca.dataset.filter_stats) + m.additional_parameters["fts_recall"] = { + "skipped": bool(getattr(self.ca.dataset, "recall_skipped", False)), + "reason": getattr(self.ca.dataset, "recall_skip_reason", None), + "serial_query_count": len(getattr(self.ca.dataset, "recall_queries_data", []) or []), + "full_query_count": len(getattr(self.ca.dataset, "queries_data", []) or []), + } if drop_old: if TaskStage.LOAD in self.config.stages: - _, load_dur = self._load_train_data() + count, load_dur = self._load_data() build_dur = self._optimize() + m.inserted_count = count m.insert_duration = round(load_dur, 4) m.optimize_duration = round(build_dur, 4) m.load_duration = round(load_dur + build_dur, 4) + m.additional_parameters.update( + { + "num_per_batch": config.NUM_PER_BATCH, + "load_concurrency": self.config.load_concurrency, + } + ) log.info( f"Finish loading the entire dataset into VectorDB," f" insert_duration={load_dur}, optimize_duration={build_dur}" @@ -203,7 +347,7 @@ def _run_perf_case(self, drop_old: bool = True) -> Metric: else: log.info("Data loading skipped") if TaskStage.SEARCH_SERIAL in self.config.stages or TaskStage.SEARCH_CONCURRENT in self.config.stages: - self._init_search_runner() + self._init_search_runners() if TaskStage.SEARCH_CONCURRENT in self.config.stages: search_results = self._conc_search() ( @@ -215,9 +359,22 @@ def _run_perf_case(self, drop_old: bool = True) -> Metric: m.conc_latency_avg_list, ) = search_results if TaskStage.SEARCH_SERIAL in self.config.stages: + cooldown = self.config.case_config.concurrency_search_config.serial_cooldown + if TaskStage.SEARCH_CONCURRENT in self.config.stages and cooldown > 0: + log.info( + f"Cooldown {cooldown}s before serial search to ensure a stable measurement environment" + ) + time.sleep(cooldown) search_results = self._serial_search() - m.recall, m.ndcg, m.serial_latency_p99, m.serial_latency_p95 = search_results - + if self.is_fts: + m.recall, m.ndcg, m.mrr, m.serial_latency_p99, m.serial_latency_p95 = search_results + else: + m.recall, m.ndcg, m.serial_latency_p99, m.serial_latency_p95 = search_results + if hasattr(self.ca, "payload_profile"): + m.payload_profile = self.ca.payload_profile.value + m.payload_estimated_bytes_per_query = self.ca.estimated_payload_bytes_per_query( + self.config.case_config.k + ) except Exception as e: log.warning(f"Failed to run performance case, reason = {e}") traceback.print_exc() @@ -239,31 +396,149 @@ def _run_streaming_case(self) -> Metric: log.info(f"Streaming case got result: {m}") return m + def _run_cloud_insert_case(self) -> Metric: + assert self.db is not None + started = time.perf_counter() + runner_kwargs = {} + if self.ca.is_multitenant: + runner_kwargs["tenant_case"] = self.ca + runner = ConcurrentInsertRunner( + self.db, + self.ca.dataset, + self.normalize, + self.ca.filters, + max_workers=self.config.load_concurrency or None, + batch_size=self.ca.batch_size, + duration=self.ca.duration, + **runner_kwargs, + ) + count = runner.task() + insert_done = time.perf_counter() + readiness_timeout = self.ca.readiness_timeout + readiness_poll_interval = self.ca.readiness_poll_interval + readiness_deadline = None if readiness_timeout is None else time.perf_counter() + readiness_timeout + with self.db.init(): + status = self.db.poll_insert_readiness(count) + searchable_started = time.perf_counter() + while not status["fully_searchable"]: + if readiness_deadline is not None and time.perf_counter() >= readiness_deadline: + msg = ( + "Cloud insert readiness timed out waiting for fully_searchable " + f"after {readiness_timeout}s; last_status={status}" + ) + raise TimeoutError(msg) + time.sleep(readiness_poll_interval) + status = self.db.poll_insert_readiness(count) + indexed_started = time.perf_counter() + while not status["fully_indexed"]: + if readiness_deadline is not None and time.perf_counter() >= readiness_deadline: + msg = ( + "Cloud insert readiness timed out waiting for fully_indexed " + f"after {readiness_timeout}s; last_status={status}" + ) + raise TimeoutError(msg) + time.sleep(readiness_poll_interval) + status = self.db.poll_insert_readiness(count) + return Metric( + inserted_count=count, + insert_rows_per_second=round(count / max(insert_done - started, 0.001), 4), + insert_completion_seconds=round(insert_done - started, 4), + searchable_after_insert_seconds=round(indexed_started - searchable_started, 4), + indexed_after_searchable_seconds=round(time.perf_counter() - indexed_started, 4), + additional_parameters=status.get("additional_parameters", {}), + ) + + def _init_cold_warm_search_runner(self) -> None: + if self.normalize: + test_emb = np.stack(self.ca.dataset.test_data) + test_emb = test_emb / np.linalg.norm(test_emb, axis=1)[:, np.newaxis] + self.test_emb = test_emb.tolist() + else: + self.test_emb = self.ca.dataset.test_data + + self.cold_warm_search_runner = ColdWarmSearchRunner( + db=self.db, + test_data=self.test_emb, + filters=self.ca.filters, + k=self.config.case_config.k, + payload_profile=self.ca.payload_profile, + query_count=self.ca.query_count, + ) + + def _run_cloud_cold_latency_case(self, drop_old: bool = True) -> Metric: + log.info("Start cloud cold latency case") + try: + self._validate_cloud_cold_latency_config(drop_old) + m = Metric() + if drop_old: + if TaskStage.LOAD in self.config.stages: + _, load_dur = self._load_train_data() + build_dur = self._optimize() + m.insert_duration = round(load_dur, 4) + m.optimize_duration = round(build_dur, 4) + m.load_duration = round(load_dur + build_dur, 4) + else: + log.info("Data loading skipped") + + self._init_cold_warm_search_runner() + m.additional_parameters = { + "cold_latency": self.cold_warm_search_runner.run(), + } + m.payload_profile = self.ca.payload_profile.value + m.payload_estimated_bytes_per_query = self.ca.estimated_payload_bytes_per_query(self.config.case_config.k) + except Exception as e: + log.warning(f"Failed to run cloud cold latency case, reason = {e}") + traceback.print_exc() + raise e from None + else: + log.info(f"Cloud cold latency case got result: {m}") + return m + @utils.time_it + def _load_data(self): + return self._load_train_data() + def _load_train_data(self): - """Insert train data and get the insert_duration""" + """Insert vector or FTS train data concurrently and get insert duration.""" try: - runner = SerialInsertRunner( + runner_kwargs = {} + if self.ca.is_multitenant: + runner_kwargs["tenant_case"] = self.ca + runner = ConcurrentInsertRunner( self.db, self.ca.dataset, self.normalize, self.ca.filters, self.ca.load_timeout, + max_workers=self.config.load_concurrency or None, + with_scalar_labels=self.ca.with_scalar_labels, + workload_kind=self.workload_kind, + **runner_kwargs, ) - runner.run() + return runner.run() except Exception as e: raise e from None finally: runner = None - def _serial_search(self) -> tuple[float, float, float, float]: + def _serial_search(self) -> tuple[float, ...]: """Performance serial tests, search the entire test data once, calculate the recall, serial_latency_p99, serial_latency_p95 Returns: - tuple[float, float, float, float]: recall, ndcg, serial_latency_p99, serial_latency_p95 + tuple[float, ...]: vector cases return recall, ndcg, p99, p95; + FTS cases return recall, p99, p95. """ try: + if self.serial_search_runner is None: + if self.is_fts and getattr(self.ca.dataset, "recall_skipped", False): + log.warning( + "Skipping FTS serial recall: %s", + getattr(self.ca.dataset, "recall_skip_reason", "unknown"), + ) + return (0.0, 0.0, 0.0, 0.0, 0.0) + msg = "serial search runner is not initialized" + raise RuntimeError(msg) # noqa: TRY301 results, _ = self.serial_search_runner.run() except Exception as e: log.warning(f"search error: {e!s}, {e}") @@ -293,19 +568,31 @@ def _optimize_task(self) -> None: self.db.optimize(data_size=self.ca.dataset.data.size) def _optimize(self) -> float: + if self.is_fts: + try: + with utils.timeout(self.ca.optimize_timeout, PerformanceTimeoutError): + _, duration = self._optimize_task() + return duration + except PerformanceTimeoutError: + log.warning(f"VectorDB optimize timeout in {self.ca.optimize_timeout}") + raise with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor: future = executor.submit(self._optimize_task) try: return future.result(timeout=self.ca.optimize_timeout)[1] except TimeoutError as e: log.warning(f"VectorDB optimize timeout in {self.ca.optimize_timeout}") - for pid, _ in executor._processes.items(): - psutil.Process(pid).kill() + kill_proc_tree(pids=list(executor._processes.keys())) raise PerformanceTimeoutError from e except Exception as e: log.warning(f"VectorDB optimize error: {e}") raise e from None + def _init_search_runners(self): + if self.is_fts: + return self._init_fts_search_runner() + return self._init_search_runner() + def _init_search_runner(self): if self.normalize: test_emb = np.stack(self.ca.dataset.test_data) @@ -314,7 +601,9 @@ def _init_search_runner(self): else: self.test_emb = self.ca.dataset.test_data - gt_df = self.ca.dataset.gt_data + tenant_labels = self.ca.tenant_labels() if self.ca.is_multitenant else None + measure_recall = getattr(self.ca, "measure_recall", True) + gt_df = self.ca.dataset.gt_data if measure_recall else None if TaskStage.SEARCH_SERIAL in self.config.stages: self.serial_search_runner = SerialSearchRunner( @@ -323,6 +612,10 @@ def _init_search_runner(self): ground_truth=gt_df, filters=self.ca.filters, k=self.config.case_config.k, + payload_profile=self.ca.payload_profile, + tenant_labels=tenant_labels, + measure_recall=measure_recall, + workload_kind=WorkloadKind.VECTOR, ) if TaskStage.SEARCH_CONCURRENT in self.config.stages: self.search_runner = MultiProcessingSearchRunner( @@ -333,6 +626,65 @@ def _init_search_runner(self): duration=self.config.case_config.concurrency_search_config.concurrency_duration, concurrency_timeout=self.config.case_config.concurrency_search_config.concurrency_timeout, k=self.config.case_config.k, + payload_profile=self.ca.payload_profile, + tenant_labels=tenant_labels, + workload_kind=WorkloadKind.VECTOR, + ) + + def _init_fts_search_runner(self): + fts_dataset = self.ca.dataset + + if fts_dataset.queries_data is None or fts_dataset.gt_data is None: + msg = "FTS dataset is missing queries or ground truth. Call prepare() before initializing search." + raise ValueError(msg) + test_texts = [q.text for q in fts_dataset.queries_data] + if len(test_texts) != len(fts_dataset.gt_data): + msg = f"FTS query count {len(test_texts)} does not match ground truth row count {len(fts_dataset.gt_data)}" + raise ValueError(msg) + + log.info(f"FTS concurrent test will use {len(test_texts)} queries for testing") + self.test_texts = test_texts + + if TaskStage.SEARCH_SERIAL in self.config.stages: + recall_queries = fts_dataset.recall_queries_data + recall_ground_truth = fts_dataset.recall_gt_data + if recall_queries is None or recall_ground_truth is None: + msg = ( + "FTS dataset is missing recall queries or ground truth. Call prepare() before initializing search." + ) + raise ValueError(msg) + if len(recall_queries) != len(recall_ground_truth): + msg = ( + f"FTS recall query count {len(recall_queries)} does not match " + f"ground truth row count {len(recall_ground_truth)}" + ) + raise ValueError(msg) + if fts_dataset.recall_skipped: + log.warning("FTS serial recall will be skipped: %s", fts_dataset.recall_skip_reason) + self.serial_search_runner = None + else: + recall_test_texts = [q.text for q in recall_queries] + log.info(f"FTS serial recall will use {len(recall_test_texts)} queries") + self.serial_search_runner = SerialSearchRunner( + db=self.db, + test_data=recall_test_texts, + ground_truth=recall_ground_truth, + filters=self.ca.filters, + k=self.config.case_config.k, + payload_profile=self.ca.payload_profile, + workload_kind=WorkloadKind.FULL_TEXT, + ) + if TaskStage.SEARCH_CONCURRENT in self.config.stages: + self.search_runner = MultiProcessingSearchRunner( + db=self.db, + test_data=test_texts, + filters=self.ca.filters, + concurrencies=self.config.case_config.concurrency_search_config.num_concurrency, + duration=self.config.case_config.concurrency_search_config.concurrency_duration, + concurrency_timeout=self.config.case_config.concurrency_search_config.concurrency_timeout, + k=self.config.case_config.k, + payload_profile=self.ca.payload_profile, + workload_kind=WorkloadKind.FULL_TEXT, ) def _init_read_write_runner(self): diff --git a/vectordb_bench/backend/utils.py b/vectordb_bench/backend/utils.py index 86c4faf5e..8c9c99a1f 100644 --- a/vectordb_bench/backend/utils.py +++ b/vectordb_bench/backend/utils.py @@ -1,6 +1,50 @@ +import contextlib +import logging +import signal +import threading import time +from collections.abc import Callable +from contextlib import contextmanager from functools import wraps +import psutil + +log = logging.getLogger(__name__) + + +def kill_proc_tree(pids: list[int] | None = None, grace: float = 2, timeout: float = 3): + """Kill child processes with SIGTERM, then SIGKILL for survivors. + + Args: + pids: Specific PIDs to kill. If None, kills all children of the + current process (recursive). + grace: Seconds to wait after SIGTERM before sending SIGKILL. + timeout: Seconds to wait for processes to fully exit after SIGKILL. + """ + if pids is not None: + targets = [] + for pid in pids: + with contextlib.suppress(psutil.NoSuchProcess): + targets.append(psutil.Process(pid)) + else: + targets = psutil.Process().children(recursive=True) + + for p in targets: + try: + log.warning(f"sending SIGTERM to child process: {p}") + p.send_signal(signal.SIGTERM) + except psutil.NoSuchProcess: + pass + + _, alive = psutil.wait_procs(targets, timeout=grace) + for p in alive: + try: + log.warning(f"force killing child process: {p}") + p.kill() + except psutil.NoSuchProcess: + pass + psutil.wait_procs(alive, timeout=timeout) + def numerize(n: int) -> str: """display positive number n for readability @@ -47,6 +91,29 @@ def inner(*args, **kwargs): return inner +@contextmanager +def timeout(timeout_seconds: float | None, exc_factory: Callable[[], Exception]): + if ( + timeout_seconds is None + or not hasattr(signal, "SIGALRM") + or threading.current_thread() is not threading.main_thread() + ): + yield + return + + def _handle_timeout(signum, frame): # noqa: ANN001, ARG001 + raise exc_factory() + + previous_handler = signal.getsignal(signal.SIGALRM) + signal.signal(signal.SIGALRM, _handle_timeout) + previous_timer = signal.setitimer(signal.ITIMER_REAL, float(timeout_seconds)) + try: + yield + finally: + signal.setitimer(signal.ITIMER_REAL, *previous_timer) + signal.signal(signal.SIGALRM, previous_handler) + + def compose_train_files(train_count: int, use_shuffled: bool) -> list[str]: prefix = "shuffle_train" if use_shuffled else "train" middle = f"of-{train_count}" diff --git a/vectordb_bench/backend/workload.py b/vectordb_bench/backend/workload.py new file mode 100644 index 000000000..e3c958b20 --- /dev/null +++ b/vectordb_bench/backend/workload.py @@ -0,0 +1,6 @@ +from enum import StrEnum + + +class WorkloadKind(StrEnum): + VECTOR = "vector" + FULL_TEXT = "full_text" diff --git a/vectordb_bench/base.py b/vectordb_bench/base.py index 694308d90..401d2086d 100644 --- a/vectordb_bench/base.py +++ b/vectordb_bench/base.py @@ -3,4 +3,4 @@ class BaseModel(PydanticBaseModel): - model_config = ConfigDict(arbitrary_types_allowed=True, validate_default=True) + model_config = ConfigDict(arbitrary_types_allowed=True) diff --git a/vectordb_bench/cli/cli.py b/vectordb_bench/cli/cli.py index 12bb4be9b..abcb91607 100644 --- a/vectordb_bench/cli/cli.py +++ b/vectordb_bench/cli/cli.py @@ -1,7 +1,6 @@ import logging import time from collections.abc import Callable -from concurrent.futures import wait from datetime import datetime from pathlib import Path from pprint import pformat @@ -15,12 +14,16 @@ ) import click +from click.core import ParameterSource from yaml import load from .. import config +from ..backend.cases import FTS_FILTER_RATES from ..backend.clients import DB -from ..backend.clients.api import MetricType -from ..interface import benchmark_runner, global_result_future +from ..backend.clients.api import IndexType, MetricType +from ..backend.dataset import DatasetWithSizeType, FtsDatasetWithSizeType +from ..backend.payload import PayloadProfile +from ..interface import benchmark_runner from ..models import ( CaseConfig, CaseType, @@ -36,6 +39,22 @@ except ImportError: from yaml import Loader +DEFAULT_DATASET_WITH_SIZE_TYPE = DatasetWithSizeType.CohereMedium.value +SUPPORTED_DATASET_WITH_SIZE_TYPES = "|".join(dataset.value for dataset in DatasetWithSizeType) +SUPPORTED_FTS_DATASET_WITH_SIZE_TYPES = "|".join(dataset.value for dataset in FtsDatasetWithSizeType) +SUPPORTED_FTS_FILTER_RATES = "|".join(f"{rate:g}" for rate in FTS_FILTER_RATES) + + +def copy_if_not_none( + custom_case_config: dict[str, Any], + parameters: dict[str, Any], + key: str, + target_key: str | None = None, +) -> None: + value = parameters[key] + if value is not None: + custom_case_config[target_key or key] = value + def click_get_defaults_from_file(ctx, param, value): # noqa: ANN001, ARG001 if value: @@ -51,6 +70,26 @@ def click_get_defaults_from_file(ctx, param, value): # noqa: ANN001, ARG001 return value +def resolve_db_note(note: str, note_file: Path | None) -> str: + ctx = click.get_current_context() + note_source = ctx.get_parameter_source("note") + note_file_source = ctx.get_parameter_source("note_file") + note_supplied = note_source not in {None, ParameterSource.DEFAULT} + note_file_supplied = note_file_source not in {None, ParameterSource.DEFAULT} + + if note_supplied and note_file_supplied: + raise click.UsageError("--note and --note-file cannot be used together") + if note_file is None: + return note + try: + content = note_file.read_text(encoding="utf-8").rstrip("\r\n") + except UnicodeDecodeError as e: + raise click.BadParameter("Note file is not valid UTF-8", param_hint="--note-file") from e + if not content.strip(): + raise click.BadParameter("Note file is empty", param_hint="--note-file") + return content + + def click_parameter_decorators_from_typed_dict( typed_dict: type, ) -> Callable[[click.decorators.FC], click.decorators.FC]: @@ -166,6 +205,7 @@ def check_custom_case_parameters(ctx: any, param: any, value: any): # noqa: ARG def get_custom_case_config(parameters: dict) -> dict: custom_case_config = {} + dataset_with_size_type = parameters["dataset_with_size_type"] or DEFAULT_DATASET_WITH_SIZE_TYPE if parameters["case_type"] == "PerformanceCustomDataset": custom_case_config = { "name": parameters["custom_case_name"], @@ -185,17 +225,120 @@ def get_custom_case_config(parameters: dict) -> dict: } elif parameters["case_type"] == "NewIntFilterPerformanceCase": custom_case_config = { - "dataset_with_size_type": parameters["dataset_with_size_type"], + "dataset_with_size_type": dataset_with_size_type, "filter_rate": parameters["filter_rate"], } elif parameters["case_type"] == "LabelFilterPerformanceCase": custom_case_config = { - "dataset_with_size_type": parameters["dataset_with_size_type"], + "dataset_with_size_type": dataset_with_size_type, "label_percentage": parameters["label_percentage"], } + elif parameters["case_type"] == "CloudPayloadSearchCase": + custom_case_config = { + "payload_profile": parameters["payload_profile"], + } + copy_if_not_none(custom_case_config, parameters, "dataset_with_size_type") + if parameters["cloud_filter_rate"] is not None: + custom_case_config["filter_rate"] = parameters["cloud_filter_rate"] + if parameters["cloud_label_percentage"] is not None: + custom_case_config["label_percentage"] = parameters["cloud_label_percentage"] + elif parameters["case_type"] == "CloudColdLatencyCase": + custom_case_config = { + "payload_profile": parameters["payload_profile"], + "query_count": parameters["cloud_cold_query_count"], + } + copy_if_not_none(custom_case_config, parameters, "dataset_with_size_type") + copy_if_not_none(custom_case_config, parameters, "cloud_filter_rate", "filter_rate") + copy_if_not_none(custom_case_config, parameters, "cloud_label_percentage", "label_percentage") + elif parameters["case_type"] == "CloudInsertCase": + custom_case_config = { + "batch_size": parameters["cloud_insert_batch_size"], + "duration": parameters["cloud_insert_duration"], + "dataset_with_size_type": dataset_with_size_type, + } + copy_if_not_none(custom_case_config, parameters, "cloud_insert_readiness_timeout", "readiness_timeout") + copy_if_not_none( + custom_case_config, + parameters, + "cloud_insert_readiness_poll_interval", + "readiness_poll_interval", + ) + elif parameters["case_type"] == "CloudMultiTenantSearchCase": + custom_case_config = { + "tenant_count": parameters["tenant_count"], + "tenant_prefix": parameters["tenant_prefix"], + "tenant_id_width": parameters["tenant_id_width"], + "payload_profile": parameters["payload_profile"], + } + copy_if_not_none(custom_case_config, parameters, "dataset_with_size_type") + if parameters["cloud_filter_rate"] is not None: + custom_case_config["filter_rate"] = parameters["cloud_filter_rate"] + if parameters["cloud_label_percentage"] is not None: + custom_case_config["label_percentage"] = parameters["cloud_label_percentage"] + elif parameters["case_type"] == "FTSBm25Performance": + dataset_with_size_type = parameters["dataset_with_size_type"] + if dataset_with_size_type not in {dataset.value for dataset in FtsDatasetWithSizeType}: + dataset_with_size_type = FtsDatasetWithSizeType.MSMarcoSmall.value + custom_case_config = { + "dataset_with_size_type": dataset_with_size_type, + "payload_profile": parameters.get("payload_profile", PayloadProfile.IDS_ONLY.value), + } + copy_if_not_none(custom_case_config, parameters, "fts_filter_rate", "filter_rate") return custom_case_config +def copy_fts_compatible_db_case_fields(source: DBCaseConfig, target: DBCaseConfig) -> DBCaseConfig: + """Copy CLI fields that remain meaningful when routing a backend to its FTS config.""" + preserved_fields = ( + "number_of_shards", + "number_of_replicas", + "refresh_interval", + "use_force_merge", + "force_merge_enabled", + "disable_backpressure", + "level", + ) + updates = { + field: getattr(source, field) for field in preserved_fields if hasattr(source, field) and hasattr(target, field) + } + if not updates: + return target + return target.model_copy(update=updates) + + +def apply_fts_cli_db_case_params( + db_case_config: DBCaseConfig, + parameters: dict[str, Any] | None, +) -> DBCaseConfig: + if not parameters: + return db_case_config + + updates = { + field: parameters[field] + for field in ("bm25_k1", "bm25_b") + if parameters.get(field) is not None and hasattr(db_case_config, field) + } + if not updates: + return db_case_config + return db_case_config.model_copy(update=updates) + + +def select_cli_db_case_config( + db: DB, + db_case_config: DBCaseConfig, + case_type: str, + parameters: dict[str, Any] | None = None, +) -> DBCaseConfig: + if case_type != CaseType.FTSBm25Performance.name: + return db_case_config + + fts_case_config_cls = db.case_config_cls(IndexType.FTS) + if isinstance(db_case_config, fts_case_config_cls): + return apply_fts_cli_db_case_params(db_case_config, parameters) + fts_db_case_config = copy_fts_compatible_db_case_fields(db_case_config, fts_case_config_cls()) + return apply_fts_cli_db_case_params(fts_db_case_config, parameters) + + log = logging.getLogger(__name__) @@ -231,6 +374,16 @@ class CommonTypedDict(TypedDict): show_default=True, ), ] + load_concurrency: Annotated[ + int, + click.option( + "--load-concurrency", + type=int, + default=config.LOAD_CONCURRENCY, + show_default=True, + help="Number of concurrent workers for data loading in performance cases (0 = cpu_count)", + ), + ] search_serial: Annotated[ bool, click.option( @@ -271,6 +424,25 @@ class CommonTypedDict(TypedDict): default=datetime.now().isoformat(), ), ] + note: Annotated[ + str, + click.option( + "--note", + type=str, + help="Run context stored with each result", + default="", + show_default=True, + ), + ] + note_file: Annotated[ + Path | None, + click.option( + "--note-file", + type=click.Path(exists=True, dir_okay=False, readable=True, path_type=Path), + help="Read run context from a UTF-8 text file", + default=None, + ), + ] dry_run: Annotated[ bool, click.option( @@ -323,6 +495,16 @@ class CommonTypedDict(TypedDict): "Set to a negative value to wait indefinitely.", ), ] + serial_cooldown: Annotated[ + float, + click.option( + "--serial-cooldown", + type=float, + default=config.SERIAL_COOLDOWN, + show_default=True, + help="Cooldown in seconds between concurrent and serial search phases", + ), + ] custom_case_name: Annotated[ str, click.option( @@ -427,14 +609,15 @@ class CommonTypedDict(TypedDict): ] task_label: Annotated[str, click.option("--task-label", help="Task label")] dataset_with_size_type: Annotated[ - str, + str | None, click.option( "--dataset-with-size-type", - help="Dataset with size type for NewIntFilterPerformanceCase/LabelFilterPerformanceCase, you can use " - "Medium Cohere (768dim, 1M)|Large Cohere (768dim, 10M)|Medium Bioasq (1024dim, 1M)|" - "Large Bioasq (1024dim, 10M)|Large OpenAI (1536dim, 5M)|Medium OpenAI (1536dim, 500K)", - default="Medium Cohere (768dim, 1M)", - show_default=True, + help="Dataset with size type. When omitted, filter/insert cases use Medium Cohere (768dim, 1M), " + "CloudPayloadSearchCase and CloudColdLatencyCase use LAION 100M, and CloudMultiTenantSearchCase " + f"uses Large Cohere (768dim, 10M). Supported vector values include " + f"{SUPPORTED_DATASET_WITH_SIZE_TYPES}. For FTSBm25Performance, supported datasets include " + f"{SUPPORTED_FTS_DATASET_WITH_SIZE_TYPES}.", + default=None, ), ] filter_rate: Annotated[ @@ -455,6 +638,141 @@ class CommonTypedDict(TypedDict): show_default=True, ), ] + payload_profile: Annotated[ + str, + click.option( + "--payload-profile", + type=click.Choice([profile.value for profile in PayloadProfile]), + help="Response payload profile for payload and FTS cases", + default="ids_only", + show_default=True, + ), + ] + bm25_k1: Annotated[ + float | None, + click.option( + "--bm25-k1", + type=float, + default=None, + help="Optional BM25 k1 override for FTS cases. Omit to use the backend default.", + ), + ] + bm25_b: Annotated[ + float | None, + click.option( + "--bm25-b", + type=float, + default=None, + help="Optional BM25 b override for FTS cases. Omit to use the backend default.", + ), + ] + fts_filter_rate: Annotated[ + float | None, + click.option( + "--fts-filter-rate", + type=float, + default=None, + help=( + "Optional FTS integer filter rate for FTSBm25Performance. " + f"Only valid for large FTS datasets. Supported values: {SUPPORTED_FTS_FILTER_RATES}." + ), + ), + ] + cloud_filter_rate: Annotated[ + float | None, + click.option( + "--cloud-filter-rate", + type=float, + default=None, + help="Optional int filter rate for CloudPayloadSearchCase and CloudColdLatencyCase", + ), + ] + cloud_label_percentage: Annotated[ + float | None, + click.option( + "--cloud-label-percentage", + type=float, + default=None, + help="Optional label percentage for CloudPayloadSearchCase and CloudColdLatencyCase", + ), + ] + cloud_cold_query_count: Annotated[ + int, + click.option( + "--cloud-cold-query-count", + type=int, + default=1000, + show_default=True, + help="Number of serial queries per cold/warm pass for CloudColdLatencyCase", + ), + ] + cloud_insert_batch_size: Annotated[ + int, + click.option( + "--cloud-insert-batch-size", + type=int, + default=5000, + show_default=True, + help="Insert batch size for CloudInsertCase", + ), + ] + cloud_insert_duration: Annotated[ + float | None, + click.option( + "--cloud-insert-duration", + type=float, + default=None, + help="Optional insert duration in seconds for CloudInsertCase", + ), + ] + cloud_insert_readiness_timeout: Annotated[ + float | None, + click.option( + "--cloud-insert-readiness-timeout", + type=float, + default=None, + help="Optional readiness polling timeout in seconds for CloudInsertCase", + ), + ] + cloud_insert_readiness_poll_interval: Annotated[ + float | None, + click.option( + "--cloud-insert-readiness-poll-interval", + type=float, + default=None, + help="Optional readiness polling interval in seconds for CloudInsertCase", + ), + ] + tenant_count: Annotated[ + int, + click.option( + "--tenant-count", + type=int, + default=1000, + show_default=True, + help="Tenant count for CloudMultiTenantSearchCase", + ), + ] + tenant_prefix: Annotated[ + str, + click.option( + "--tenant-prefix", + type=str, + default="tenant_", + show_default=True, + help="Tenant label prefix for CloudMultiTenantSearchCase", + ), + ] + tenant_id_width: Annotated[ + int, + click.option( + "--tenant-id-width", + type=int, + default=4, + show_default=True, + help="Zero-padding width for CloudMultiTenantSearchCase tenant IDs", + ), + ] class HNSWBaseTypedDict(TypedDict): @@ -623,10 +941,14 @@ def run( **parameters: expects keys from CommonTypedDict """ + db_config = db_config.model_copy( + update={"note": resolve_db_note(parameters["note"], parameters["note_file"])}, + ) + task = TaskConfig( db=db, db_config=db_config, - db_case_config=db_case_config, + db_case_config=select_cli_db_case_config(db, db_case_config, parameters["case_type"], parameters), case_config=CaseConfig( case_id=CaseType[parameters["case_type"]], k=parameters["k"], @@ -634,6 +956,7 @@ def run( concurrency_duration=parameters["concurrency_duration"], num_concurrency=[int(s) for s in parameters["num_concurrency"]], concurrency_timeout=parameters["concurrency_timeout"], + serial_cooldown=parameters["serial_cooldown"], ), custom_case=get_custom_case_config(parameters), ), @@ -643,15 +966,16 @@ def run( parameters["search_serial"], parameters["search_concurrent"], ), + load_concurrency=parameters["load_concurrency"], ) task_label = parameters["task_label"] log.info(f"Task:\n{pformat(task)}\n") if not parameters["dry_run"]: benchmark_runner.run([task], task_label) - time.sleep(5) - if global_result_future: - wait([global_result_future]) - - while benchmark_runner.has_running(): - time.sleep(1) + try: + while benchmark_runner.has_running(): + time.sleep(1) + except KeyboardInterrupt: + log.warning("Ctrl+C received, stopping benchmark...") + benchmark_runner.stop_running() diff --git a/vectordb_bench/cli/vectordbbench.py b/vectordb_bench/cli/vectordbbench.py index 751eaed66..261f3ebff 100644 --- a/vectordb_bench/cli/vectordbbench.py +++ b/vectordb_bench/cli/vectordbbench.py @@ -1,3 +1,4 @@ +from ..backend.clients.adbpg.cli import AdbpgNova from ..backend.clients.alisql.cli import AliSQLHNSW from ..backend.clients.alloydb.cli import AlloyDBScaNN from ..backend.clients.antfly.cli import AntflyAKNN @@ -14,11 +15,17 @@ ) from ..backend.clients.endee.cli import Endee from ..backend.clients.hologres.cli import HologresHGraph -from ..backend.clients.lancedb.cli import LanceDB +from ..backend.clients.lancedb.cli import ( + LanceDB, + LanceDBAutoIndex, + LanceDBIVFHNSWPQ, + LanceDBIVFHNSWSQ, + LanceDBIVFPQ, +) from ..backend.clients.lindorm.cli import LindormHNSW, LindormIVFBQ, LindormIVFPQ from ..backend.clients.mariadb.cli import MariaDBHNSW from ..backend.clients.memorydb.cli import MemoryDB -from ..backend.clients.milvus.cli import MilvusAutoIndex +from ..backend.clients.milvus.cli import MilvusAutoIndex, MilvusFTS from ..backend.clients.oceanbase.cli import OceanBaseHNSW, OceanBaseIVF from ..backend.clients.oss_opensearch.cli import OSSOpenSearch from ..backend.clients.pgdiskann.cli import PgDiskAnn @@ -26,21 +33,31 @@ from ..backend.clients.pgvector.cli import PgVectorHNSW from ..backend.clients.pgvectorscale.cli import PgVectorScaleDiskAnn from ..backend.clients.pinecone.cli import Pinecone +from ..backend.clients.pinot.cli import Pinot +from ..backend.clients.polardb.cli import ( + PolarDBHNSWFlat, + PolarDBHNSWPQ, + PolarDBHNSWSQ, +) from ..backend.clients.qdrant_cloud.cli import QdrantCloud from ..backend.clients.qdrant_local.cli import QdrantLocal from ..backend.clients.redis.cli import Redis from ..backend.clients.s3_vectors.cli import S3Vectors +from ..backend.clients.seekdb.cli import SeekDBHNSW from ..backend.clients.tencent_elasticsearch.cli import TencentElasticsearch from ..backend.clients.test.cli import Test from ..backend.clients.tidb.cli import TiDB -from ..backend.clients.turbopuffer.cli import TurboPuffer +from ..backend.clients.turbopuffer.cli import TurboPuffer, TurboPufferUnpin +from ..backend.clients.vectorchord.cli import VectorChordGraph, VectorChordRQ from ..backend.clients.vespa.cli import Vespa +from ..backend.clients.volc_mysql.cli import VolcMySQLHNSW from ..backend.clients.weaviate_cloud.cli import Weaviate from ..backend.clients.zilliz_cloud.cli import ZillizAutoIndex from ..backend.clients.zvec.cli import Zvec from .batch_cli import BatchCli from .cli import cli +cli.add_command(AdbpgNova) cli.add_command(PgVectorHNSW) cli.add_command(PgVectoRSHNSW) cli.add_command(PgVectoRSIVFFlat) @@ -50,6 +67,7 @@ cli.add_command(Test) cli.add_command(ZillizAutoIndex) cli.add_command(MilvusAutoIndex) +cli.add_command(MilvusFTS) cli.add_command(AWSOpenSearch) cli.add_command(OSSOpenSearch) cli.add_command(PgVectorScaleDiskAnn) @@ -63,6 +81,10 @@ cli.add_command(Clickhouse) cli.add_command(Vespa) cli.add_command(LanceDB) +cli.add_command(LanceDBAutoIndex) +cli.add_command(LanceDBIVFPQ) +cli.add_command(LanceDBIVFHNSWSQ) +cli.add_command(LanceDBIVFHNSWPQ) cli.add_command(HologresHGraph) cli.add_command(QdrantCloud) cli.add_command(QdrantLocal) @@ -76,6 +98,7 @@ cli.add_command(AliSQLHNSW) cli.add_command(Doris) cli.add_command(TurboPuffer) +cli.add_command(TurboPufferUnpin) cli.add_command(Chroma) cli.add_command(Zvec) cli.add_command(Endee) @@ -83,6 +106,14 @@ cli.add_command(LindormHNSW) cli.add_command(LindormIVFBQ) cli.add_command(Pinecone) +cli.add_command(VectorChordRQ) +cli.add_command(VectorChordGraph) +cli.add_command(Pinot) +cli.add_command(PolarDBHNSWFlat) +cli.add_command(PolarDBHNSWPQ) +cli.add_command(PolarDBHNSWSQ) +cli.add_command(SeekDBHNSW) +cli.add_command(VolcMySQLHNSW) cli.add_command(AntflyAKNN) diff --git a/vectordb_bench/fig/homepage/full_text_search.png b/vectordb_bench/fig/homepage/full_text_search.png new file mode 100644 index 000000000..fa9b19af8 Binary files /dev/null and b/vectordb_bench/fig/homepage/full_text_search.png differ diff --git a/vectordb_bench/frontend/components/check_results/charts.py b/vectordb_bench/frontend/components/check_results/charts.py index 0e74d2752..d36cc5fc6 100644 --- a/vectordb_bench/frontend/components/check_results/charts.py +++ b/vectordb_bench/frontend/components/check_results/charts.py @@ -153,4 +153,4 @@ def drawMetricChart(data, metric, st, key: str): ), ) - chart.plotly_chart(fig, use_container_width=True, key=key) + chart.plotly_chart(fig, width="stretch", key=key) diff --git a/vectordb_bench/frontend/components/check_results/filters.py b/vectordb_bench/frontend/components/check_results/filters.py index 6016c0040..e42a63fd6 100644 --- a/vectordb_bench/frontend/components/check_results/filters.py +++ b/vectordb_bench/frontend/components/check_results/filters.py @@ -1,4 +1,4 @@ -from vectordb_bench.backend.cases import Case +from vectordb_bench.backend.cases import Case, CaseLabel from vectordb_bench.backend.dataset import DatasetWithSizeType from vectordb_bench.backend.filter import FilterOp from vectordb_bench.frontend.components.check_results.data import getChartData @@ -75,6 +75,10 @@ def getShowDbsAndCases(st, result: list[CaseResult], filter_type: FilterOp) -> t ) showCaseNames = [] + # Handle FTS cases separately + fts_cases = [case for case in allCases if case.label == CaseLabel.FullTextSearchPerformance] + non_fts_cases = [case for case in allCases if case.label != CaseLabel.FullTextSearchPerformance] + if filter_type == FilterOp.NonFilter: allCaseNameSet = set({case.name for case in allCases}) allCaseNames = [case_name for case_name in CASE_NAME_ORDER if case_name in allCaseNameSet] + [ @@ -101,7 +105,10 @@ def getShowDbsAndCases(st, result: list[CaseResult], filter_type: FilterOp) -> t optionLables=[v.value for v in datasetWithSizeTypes], ) datasets = [dataset_with_size_type.get_manager() for dataset_with_size_type in showDatasetWithSizeTypes] - showCaseNames = list(set([case.name for case in allCases if case.dataset in datasets])) + showCaseNames = list(set([case.name for case in non_fts_cases if case.dataset in datasets])) + # Add FTS cases + fts_case_names = [case.name for case in fts_cases] + showCaseNames.extend(fts_case_names) return showDBNames, showCaseNames diff --git a/vectordb_bench/frontend/components/check_results/nav.py b/vectordb_bench/frontend/components/check_results/nav.py index ba4fa99c7..a6515ac24 100644 --- a/vectordb_bench/frontend/components/check_results/nav.py +++ b/vectordb_bench/frontend/components/check_results/nav.py @@ -1,25 +1,22 @@ -from streamlit_extras.switch_page_button import switch_page - - def NavToRunTest(st): st.subheader("Run your test") st.write("You can set the configs and run your own test.") navClick = st.button("Run Your Test   >") if navClick: - switch_page("run test") + st.switch_page("pages/run_test.py") def NavToQuriesPerDollar(st): st.subheader("Compare qps with price.") navClick = st.button("QP$ (Quries per Dollar)   >") if navClick: - switch_page("quries_per_dollar") + st.switch_page("pages/quries_per_dollar.py") def NavToResults(st, key="nav-to-results"): navClick = st.button("<   Back to Results", key=key) if navClick: - switch_page("results") + st.switch_page("pages/results.py") def NavToPages(st): @@ -27,6 +24,7 @@ def NavToPages(st): {"name": "Run Test", "link": "run_test"}, {"name": "Results", "link": "results"}, {"name": "Qps & Recall", "link": "qps_recall"}, + {"name": "Full Text Search", "link": "full_text_search"}, {"name": "Quries Per Dollar", "link": "quries_per_dollar"}, {"name": "Concurrent", "link": "concurrent"}, {"name": "Label Filter", "link": "label_filter"}, diff --git a/vectordb_bench/frontend/components/check_results/priceTable.py b/vectordb_bench/frontend/components/check_results/priceTable.py index f2c0ae001..f34f70872 100644 --- a/vectordb_bench/frontend/components/check_results/priceTable.py +++ b/vectordb_bench/frontend/components/check_results/priceTable.py @@ -27,7 +27,7 @@ def priceTable(container, data): expander = container.expander("Price List (Editable).") editTable = expander.data_editor( table, - use_container_width=True, + width="stretch", hide_index=True, height=height, disabled=("DB", "Label"), diff --git a/vectordb_bench/frontend/components/concurrent/charts.py b/vectordb_bench/frontend/components/concurrent/charts.py index 004fcb261..5369d5912 100644 --- a/vectordb_bench/frontend/components/concurrent/charts.py +++ b/vectordb_bench/frontend/components/concurrent/charts.py @@ -94,4 +94,4 @@ def drawChart(data, st, key: str, x_metric: str = "latency_p99", y_metric: str = fig.update_yaxes(range=yrange, title_text=gen_title(y_metric)) fig.update_traces(textposition="bottom right", texttemplate="conc-%{text:,.4~r}") - st.plotly_chart(fig, use_container_width=True, key=key) + st.plotly_chart(fig, width="stretch", key=key) diff --git a/vectordb_bench/frontend/components/int_filter/charts.py b/vectordb_bench/frontend/components/int_filter/charts.py index 881681031..5c32a089e 100644 --- a/vectordb_bench/frontend/components/int_filter/charts.py +++ b/vectordb_bench/frontend/components/int_filter/charts.py @@ -57,4 +57,4 @@ def drawChart(st, data: list[object], metric): margin=dict(l=0, r=0, t=40, b=0, pad=8), legend=dict(orientation="h", yanchor="bottom", y=1, xanchor="right", x=1, title=""), ) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") diff --git a/vectordb_bench/frontend/components/label_filter/charts.py b/vectordb_bench/frontend/components/label_filter/charts.py index 881681031..5c32a089e 100644 --- a/vectordb_bench/frontend/components/label_filter/charts.py +++ b/vectordb_bench/frontend/components/label_filter/charts.py @@ -57,4 +57,4 @@ def drawChart(st, data: list[object], metric): margin=dict(l=0, r=0, t=40, b=0, pad=8), legend=dict(orientation="h", yanchor="bottom", y=1, xanchor="right", x=1, title=""), ) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") diff --git a/vectordb_bench/frontend/components/qps_recall/charts.py b/vectordb_bench/frontend/components/qps_recall/charts.py index ab57dd0ce..a44c51a36 100644 --- a/vectordb_bench/frontend/components/qps_recall/charts.py +++ b/vectordb_bench/frontend/components/qps_recall/charts.py @@ -115,4 +115,4 @@ def drawlinechart(st, data: list[object], metric, key: str): margin=dict(l=0, r=0, t=40, b=0, pad=8), legend=dict(orientation="h", yanchor="bottom", y=1, xanchor="right", x=1, title=""), ) - st.plotly_chart(fig, use_container_width=True, key=key) + st.plotly_chart(fig, width="stretch", key=key) diff --git a/vectordb_bench/frontend/components/run_test/autoRefresh.py b/vectordb_bench/frontend/components/run_test/autoRefresh.py deleted file mode 100644 index 034ab5017..000000000 --- a/vectordb_bench/frontend/components/run_test/autoRefresh.py +++ /dev/null @@ -1,10 +0,0 @@ -from streamlit_autorefresh import st_autorefresh -from vectordb_bench.frontend.config.styles import * - - -def autoRefresh(): - auto_refresh_count = st_autorefresh( - interval=MAX_AUTO_REFRESH_INTERVAL, - limit=MAX_AUTO_REFRESH_COUNT, - key="streamlit-auto-refresh", - ) diff --git a/vectordb_bench/frontend/components/run_test/caseSelector.py b/vectordb_bench/frontend/components/run_test/caseSelector.py index 2e104ce54..685a38ec2 100644 --- a/vectordb_bench/frontend/components/run_test/caseSelector.py +++ b/vectordb_bench/frontend/components/run_test/caseSelector.py @@ -8,6 +8,7 @@ get_case_config_inputs, get_custom_case_cluter, get_custom_streaming_case_cluster, + get_selectable_case_items, ) from vectordb_bench.frontend.config.styles import ( CASE_CONFIG_SETTING_COLUMNS, @@ -47,7 +48,7 @@ def caseSelector(st, activedDbList: list[DB]): def caseClusterExpander(st, caseCluster: UICaseItemCluster, dbToCaseClusterConfigs, activedDbList: list[DB]): expander = st.expander(caseCluster.label, False) activedCases: list[CaseConfig] = [] - for uiCaseItem in caseCluster.uiCaseItems: + for uiCaseItem in get_selectable_case_items(caseCluster, activedDbList): if uiCaseItem.isLine: addHorizontalLine(expander) else: diff --git a/vectordb_bench/frontend/components/run_test/dbConfigSetting.py b/vectordb_bench/frontend/components/run_test/dbConfigSetting.py index a2d2de77f..85167fb66 100644 --- a/vectordb_bench/frontend/components/run_test/dbConfigSetting.py +++ b/vectordb_bench/frontend/components/run_test/dbConfigSetting.py @@ -11,19 +11,18 @@ def dbConfigSettings(st, activedDbList: list[DB]): isAllValid = True for activeDb in activedDbList: dbConfigSettingItemContainer = expander.container() - dbConfig = dbConfigSettingItem(dbConfigSettingItemContainer, activeDb) try: + dbConfig = dbConfigSettingItem(dbConfigSettingItemContainer, activeDb) dbConfigs[activeDb] = activeDb.config_cls(**dbConfig) + # Probe client module so missing optional deps surface now, not on Run. + _ = activeDb.init_cls + except ModuleNotFoundError as e: + isAllValid = False + dbConfigSettingItemContainer.error(f"{activeDb.value} needs `{e.name}` but it is not installed.") except ValidationError as e: isAllValid = False - errTexts = [] - for err in e.raw_errors: - errLocs = err.loc_tuple() - errInfo = err.exc - errText = f"{', '.join(errLocs)} - {errInfo}" - errTexts.append(errText) - - dbConfigSettingItemContainer.error(f"{'; '.join(errTexts)}") + errTexts = [f"{', '.join(str(x) for x in err['loc'])} - {err['msg']}" for err in e.errors()] + dbConfigSettingItemContainer.error("; ".join(errTexts)) return dbConfigs, isAllValid diff --git a/vectordb_bench/frontend/components/run_test/generateTasks.py b/vectordb_bench/frontend/components/run_test/generateTasks.py index 725dea769..5d848bb94 100644 --- a/vectordb_bench/frontend/components/run_test/generateTasks.py +++ b/vectordb_bench/frontend/components/run_test/generateTasks.py @@ -1,4 +1,5 @@ from vectordb_bench.backend.clients import DB +from vectordb_bench.backend.cases import CaseLabel from vectordb_bench.models import CaseConfig, CaseConfigParamType, TaskConfig @@ -6,18 +7,34 @@ def generate_tasks(activedDbList: list[DB], dbConfigs, activedCaseList: list[Cas tasks = [] for db in activedDbList: for case in activedCaseList: + # Get the index type for this case + index_type = allCaseConfigs[db][case].get(CaseConfigParamType.IndexType, None) + + # Special handling for FTS cases + if case.case.label == CaseLabel.FullTextSearchPerformance: + from vectordb_bench.backend.clients.api import IndexType + + index_type = IndexType.FTS + elif index_type is None: + # Default to AUTOINDEX for cases without specific index type + from vectordb_bench.backend.clients.api import IndexType + + index_type = IndexType.AUTOINDEX + + # Create the database case config cfg = {key.value: value for key, value in allCaseConfigs[db][case].items()} # Many DBCaseConfig models require an `index` field, while the UI stores the selection under `IndexType`. # Passing both keeps backwards-compatibility (extra fields are ignored) and enables strict models (e.g. OceanBase). if CaseConfigParamType.IndexType in allCaseConfigs[db][case] and "index" not in cfg: cfg["index"] = allCaseConfigs[db][case][CaseConfigParamType.IndexType] + + db_case_config = db.case_config_cls(index_type)(**cfg) + task = TaskConfig( db=db.value, db_config=dbConfigs[db], case_config=case, - db_case_config=db.case_config_cls(allCaseConfigs[db][case].get(CaseConfigParamType.IndexType, None))( - **cfg - ), + db_case_config=db_case_config, ) tasks.append(task) diff --git a/vectordb_bench/frontend/components/run_test/submitTask.py b/vectordb_bench/frontend/components/run_test/submitTask.py index 01d0c5876..93fd280c3 100644 --- a/vectordb_bench/frontend/components/run_test/submitTask.py +++ b/vectordb_bench/frontend/components/run_test/submitTask.py @@ -1,71 +1,76 @@ from datetime import datetime + +import streamlit as st + from vectordb_bench import config from vectordb_bench.frontend.config import styles from vectordb_bench.interface import benchmark_runner from vectordb_bench.models import TaskConfig -def submitTask(st, tasks, isAllValid): - st.markdown( +def submitTask(container, tasks, isAllValid): + container.markdown( "
", unsafe_allow_html=True, ) - st.subheader("STEP 3: Task Label") - st.markdown( + container.subheader("STEP 3: Task Label") + container.markdown( "
This description is used to mark the result.
", unsafe_allow_html=True, ) - taskLabel = taskLabelInput(st) + taskLabel = taskLabelInput(container) - st.markdown( + container.markdown( "
", unsafe_allow_html=True, ) - controlPanelContainer = st.container() - controlPanel(controlPanelContainer, tasks, taskLabel, isAllValid) + controlPanel(container.container(), tasks, taskLabel, isAllValid) -def taskLabelInput(st): +def taskLabelInput(container): defaultTaskLabel = datetime.now().strftime("%Y%m%d%H") - columns = st.columns(styles.TASK_LABEL_INPUT_COLUMNS) - taskLabel = columns[0].text_input("task_label", defaultTaskLabel, label_visibility="collapsed") - return taskLabel + cols = container.columns(styles.TASK_LABEL_INPUT_COLUMNS) + return cols[0].text_input("task_label", defaultTaskLabel, label_visibility="collapsed") -def advancedSettings(st): - container = st.columns([1, 2]) - index_already_exists = container[0].checkbox("Index already exists", value=False) - container[1].caption("if selected, inserting and building will be skipped.") +def advancedSettings(container): + cols = container.columns([1, 2]) + index_already_exists = cols[0].checkbox("Index already exists", value=False) + cols[1].caption("if selected, inserting and building will be skipped.") - container = st.columns([1, 2]) - use_aliyun = container[0].checkbox("Dataset from Aliyun (Shanghai)", value=False) - container[1].caption( - "if selected, the dataset will be downloaded from Aliyun OSS shanghai, default AWS S3 aws-us-west." - ) + cols = container.columns([1, 2]) + use_aliyun = cols[0].checkbox("Dataset from Aliyun (Shanghai)", value=False) + cols[1].caption("if selected, the dataset will be downloaded from Aliyun OSS shanghai, default AWS S3 aws-us-west.") - container = st.columns([1, 2]) - k = container[0].number_input("k", min_value=1, value=100, label_visibility="collapsed") - container[1].caption("K value for number of nearest neighbors to search") + cols = container.columns([1, 2]) + k = cols[0].number_input("k", min_value=1, value=100, label_visibility="collapsed") + cols[1].caption("K value for number of nearest neighbors to search") - container = st.columns([1, 2]) + cols = container.columns([1, 2]) defaultconcurrentInput = ",".join(map(str, config.NUM_CONCURRENCY)) - concurrentInput = container[0].text_input( - "Concurrent Input", value=defaultconcurrentInput, label_visibility="collapsed" - ) - container[1].caption("num of concurrencies for search tests to get max-qps") + concurrentInput = cols[0].text_input("Concurrent Input", value=defaultconcurrentInput, label_visibility="collapsed") + cols[1].caption("num of concurrencies for search tests to get max-qps") - container = st.columns([1, 2]) - concurrency_duration = container[0].number_input( + cols = container.columns([1, 2]) + concurrency_duration = cols[0].number_input( "Concurrency Duration", value=config.CONCURRENCY_DURATION, label_visibility="collapsed" ) - container[1].caption("concurrency duration for each concurrency search test") - return index_already_exists, use_aliyun, k, concurrentInput, concurrency_duration + cols[1].caption("concurrency duration for each concurrency search test") + + cols = container.columns([1, 2]) + load_concurrency = cols[0].number_input( + "Load Concurrency", min_value=0, value=config.LOAD_CONCURRENCY, label_visibility="collapsed" + ) + cols[1].caption("number of concurrent workers for data loading in performance cases (0 = cpu_count)") + return index_already_exists, use_aliyun, k, concurrentInput, concurrency_duration, load_concurrency -def controlPanel(st, tasks: list[TaskConfig], taskLabel, isAllValid): - index_already_exists, use_aliyun, k, concurrentInput, concurrency_duration = advancedSettings(st) +def controlPanel(container, tasks: list[TaskConfig], taskLabel, isAllValid): + index_already_exists, use_aliyun, k, concurrentInput, concurrency_duration, load_concurrency = advancedSettings( + container + ) def runHandler(): benchmark_runner.set_drop_old(not index_already_exists) @@ -73,54 +78,57 @@ def runHandler(): try: concurrentInput_list = [int(item.strip()) for item in concurrentInput.split(",")] except ValueError: - st.write("please input correct number") + container.write("please input correct number") return None for task in tasks: task.case_config.k = k task.case_config.concurrency_search_config.num_concurrency = concurrentInput_list task.case_config.concurrency_search_config.concurrency_duration = concurrency_duration + task.load_concurrency = load_concurrency benchmark_runner.set_download_address(use_aliyun) benchmark_runner.run(tasks, taskLabel) def stopHandler(): benchmark_runner.stop_running() - isRunning = benchmark_runner.has_running() - - if isRunning: - currentTaskId = benchmark_runner.get_current_task_id() - tasksCount = benchmark_runner.get_tasks_count() - text = f":running: Running Task {currentTaskId} / {tasksCount}" - - if tasksCount > 0: - st.progress(currentTaskId / tasksCount, text=text) - - columns = st.columns(6) - columns[0].button( - "Run Your Test", - disabled=True, - on_click=runHandler, - type="primary", - ) - columns[1].button( - "Stop", - on_click=stopHandler, - type="primary", - ) - - else: - errorText = benchmark_runner.latest_error or "" - if len(errorText) > 0: - st.error(errorText) - disabled = True if len(tasks) == 0 or not isAllValid else False - if not isAllValid: - st.error("Make sure all config is valid.") - elif len(tasks) == 0: - st.warning("No tests to run.") - st.button( - "Run Your Test", - disabled=disabled, - on_click=runHandler, - type="primary", - ) + @st.fragment(run_every=f"{styles.MAX_AUTO_REFRESH_INTERVAL / 1000}s") + def _renderLiveStatus(): + if benchmark_runner.has_running(): + currentTaskId = benchmark_runner.get_current_task_id() + tasksCount = benchmark_runner.get_tasks_count() + text = f":running: Running Task {currentTaskId} / {tasksCount}" + if tasksCount > 0: + st.progress(currentTaskId / tasksCount, text=text) + cols = st.columns(6) + cols[0].button( + "Run Your Test", + disabled=True, + on_click=runHandler, + type="primary", + key="run-disabled", + ) + cols[1].button( + "Stop", + on_click=stopHandler, + type="primary", + key="stop-btn", + ) + else: + errorText = benchmark_runner.latest_error or "" + if len(errorText) > 0: + st.error(errorText) + disabled = len(tasks) == 0 or not isAllValid + if not isAllValid: + st.error("Make sure all config is valid.") + elif len(tasks) == 0: + st.warning("No tests to run.") + st.button( + "Run Your Test", + disabled=disabled, + on_click=runHandler, + type="primary", + key="run-btn", + ) + + _renderLiveStatus() diff --git a/vectordb_bench/frontend/components/streaming/charts.py b/vectordb_bench/frontend/components/streaming/charts.py index a05da9b25..09357cf44 100644 --- a/vectordb_bench/frontend/components/streaming/charts.py +++ b/vectordb_bench/frontend/components/streaming/charts.py @@ -119,7 +119,7 @@ def drawLineChart( if x_metric == DisplayedMetric.search_time: x_title = "Actual Time (s)" fig.update_layout(xaxis_title=x_title) - st.plotly_chart(fig, use_container_width=True, key=key) + st.plotly_chart(fig, width="stretch", key=key) def get_normal_scatter( @@ -234,7 +234,7 @@ def drawBarChart( fig.update_layout(xaxis_title="time (s)") fig.update_layout(barmode="stack") fig.update_traces(width=0.15) - st.plotly_chart(fig, use_container_width=True, key=key) + st.plotly_chart(fig, width="stretch", key=key) def get_bar( diff --git a/vectordb_bench/frontend/components/streaming/concurrent_detail.py b/vectordb_bench/frontend/components/streaming/concurrent_detail.py index 0580bad14..37852a7ab 100644 --- a/vectordb_bench/frontend/components/streaming/concurrent_detail.py +++ b/vectordb_bench/frontend/components/streaming/concurrent_detail.py @@ -124,7 +124,7 @@ def drawQPSLatencyChart(container, qps_values, latencies_ms, stage, metric_name, showlegend=False, ) - container.plotly_chart(fig, use_container_width=True, key=f"{case_name}-chart-{stage}") + container.plotly_chart(fig, width="stretch", key=f"{case_name}-chart-{stage}") def drawMetricsTable(container, qps_values, conc_nums, p99_list, p95_list, avg_list, stage): @@ -267,7 +267,7 @@ def drawComparisonChart(container, case_data, selected_stages, metric_name, case legend=dict(yanchor="top", y=0.99, xanchor="right", x=0.99), ) - container.plotly_chart(fig, use_container_width=True, key=f"{case_name}-compare-chart") + container.plotly_chart(fig, width="stretch", key=f"{case_name}-compare-chart") # Add insight container.info("**Insight:** Compare curves across stages to understand how performance scales with data growth.") diff --git a/vectordb_bench/frontend/components/welcome/welcomePrams.py b/vectordb_bench/frontend/components/welcome/welcomePrams.py index 48bf5995c..6a7cf48e0 100644 --- a/vectordb_bench/frontend/components/welcome/welcomePrams.py +++ b/vectordb_bench/frontend/components/welcome/welcomePrams.py @@ -80,6 +80,16 @@ def welcomePrams(st): "image": "fig/homepage/table.png", "link": "tables", }, + { + "title": "FullTextSearch Performance", + "description": ( + "" + "To view BM25 full text search performance across datasets, payload modes, and backends." + "" + ), + "image": "fig/homepage/full_text_search.png", + "link": "full_text_search", + }, { "title": "Concurrent Performance", "description": ( @@ -148,7 +158,7 @@ def welcomePrams(st): for option in options: option["image"] = get_image_as_base64(option["image"]) - for option in options[:7]: + for option in options[:8]: html_content += f"""
@@ -167,7 +177,7 @@ def welcomePrams(st):
""" - for option in options[7:9]: + for option in options[8:10]: html_content += f"""
diff --git a/vectordb_bench/frontend/config/dbCaseConfigs.py b/vectordb_bench/frontend/config/dbCaseConfigs.py index 825f89893..daacae26a 100644 --- a/vectordb_bench/frontend/config/dbCaseConfigs.py +++ b/vectordb_bench/frontend/config/dbCaseConfigs.py @@ -4,7 +4,7 @@ from vectordb_bench.backend.cases import CaseLabel, CaseType from vectordb_bench.backend.clients import DB from vectordb_bench.backend.clients.api import IndexType, MetricType, SQType -from vectordb_bench.backend.dataset import DatasetWithSizeType +from vectordb_bench.backend.dataset import DatasetWithSizeType, FtsDatasetWithSizeType from vectordb_bench.frontend.components.custom.getCustomConfig import get_custom_configs from vectordb_bench.models import CaseConfig, CaseConfigParamType @@ -12,6 +12,7 @@ MAX_STREAMLIT_INT = (1 << 53) - 1 DB_LIST = [d for d in DB if d != DB.Test] +FTS_SUPPORTED_DBS = {DB.Milvus, DB.ZillizCloud, DB.ElasticCloud, DB.OSSOpenSearch, DB.Vespa, DB.TurboPuffer} class Delimiter(Enum): @@ -52,6 +53,7 @@ class UICaseItem(BaseModel): description: str = "" cases: list[CaseConfig] = [] caseLabel: CaseLabel = CaseLabel.Performance + supportedDbs: list[DB] | None = None extra_custom_case_config_inputs: list[ConfigInput] = [] tmp_custom_config: dict = dict() @@ -104,12 +106,21 @@ def get_cases(self) -> list[CaseConfig]: ] return cases + def supports_dbs(self, dbs: list[DB]) -> bool: + if self.supportedDbs is None: + return True + return all(db in self.supportedDbs for db in dbs) + class UICaseItemCluster(BaseModel): label: str = "" uiCaseItems: list[UICaseItem] = [] +def get_selectable_case_items(caseCluster: UICaseItemCluster, activedDbList: list[DB]) -> list[UICaseItem]: + return [uiCaseItem for uiCaseItem in caseCluster.uiCaseItems if uiCaseItem.supports_dbs(activedDbList)] + + def get_custom_case_items() -> list[UICaseItem]: custom_configs = get_custom_configs() return [ @@ -157,6 +168,30 @@ def generate_normal_cases(case_id: CaseType, custom_case: dict | None = None) -> return [CaseConfig(case_id=case_id, custom_case=custom_case)] +def generate_fts_case(dataset_with_size_type: FtsDatasetWithSizeType) -> CaseConfig: + return CaseConfig( + case_id=CaseType.FTSBm25Performance, + custom_case={"dataset_with_size_type": dataset_with_size_type.value}, + ) + + +def get_fts_case_items() -> list[UICaseItem]: + dataset_with_size_types = list(FtsDatasetWithSizeType) + return [ + UICaseItem( + label=f"FTS BM25 Performance - {dataset_with_size_type.value}", + description=( + f"This case tests native BM25 full-text search performance on {dataset_with_size_type.value}. " + "It measures index building time, recall, serial latency, and search QPS." + ), + cases=[generate_fts_case(dataset_with_size_type)], + caseLabel=CaseLabel.FullTextSearchPerformance, + supportedDbs=list(FTS_SUPPORTED_DBS), + ) + for dataset_with_size_type in dataset_with_size_types + ] + + def get_custom_case_cluter() -> UICaseItemCluster: return UICaseItemCluster(label="Custom Search Performance Test", uiCaseItems=get_custom_case_items()) @@ -358,6 +393,10 @@ def generate_int_filter_cases(dataset_with_size_type: DatasetWithSizeType) -> li ) ], ), + UICaseItemCluster( + label="Full-Text Search (FTS) Test", + uiCaseItems=get_fts_case_items(), + ), ] # DIVIDER = "DIVIDER" @@ -372,6 +411,7 @@ def generate_int_filter_cases(dataset_with_size_type: DatasetWithSizeType) -> li CaseType.Performance1024D10M, CaseType.CapacityDim960, CaseType.CapacityDim128, + CaseType.FTSBm25Performance, ] CASE_NAME_ORDER = [case.case_cls().name for case in DISPLAY_CASE_ORDER] @@ -379,15 +419,6 @@ def generate_int_filter_cases(dataset_with_size_type: DatasetWithSizeType) -> li # item for item in CASE_LIST_WITH_DIVIDER if isinstance(item, CaseType)] -class InputType(IntEnum): - Text = 20001 - Number = 20002 - Option = 20003 - Float = 20004 - Bool = 20005 - Select = 20006 - - class CaseConfigInput(BaseModel): label: CaseConfigParamType inputType: InputType = InputType.Text @@ -517,7 +548,7 @@ class CaseConfigInput(BaseModel): displayLabel="Reranking Metric", inputType=InputType.Option, inputConfig={ - "options": [metric.value for metric in MetricType if metric.value not in ["HAMMING", "JACCARD", "DP"]], + "options": [metric.value for metric in MetricType if metric.value not in ["HAMMING", "JACCARD", "DP", "BM25"]], }, isDisplayed=lambda config: config.get(CaseConfigParamType.reranking, False), ) @@ -1467,7 +1498,7 @@ class CaseConfigInput(BaseModel): label=CaseConfigParamType.rerankingMetric, inputType=InputType.Option, inputConfig={ - "options": [metric.value for metric in MetricType if metric.value not in ["HAMMING", "JACCARD"]], + "options": [metric.value for metric in MetricType if metric.value not in ["HAMMING", "JACCARD", "BM25"]], }, isDisplayed=lambda config: config.get(CaseConfigParamType.quantizationType, None) == "bit" and config.get(CaseConfigParamType.reranking, False), @@ -2119,6 +2150,113 @@ class CaseConfigInput(BaseModel): ), ) +CaseConfigParamInput_IndexType_FTS = CaseConfigInput( + label=CaseConfigParamType.IndexType, + inputHelp="FTS Index Type (currently only AUTOINDEX supported)", + inputType=InputType.Option, + inputConfig={ + "options": [ + IndexType.FTS.value, + ], + }, + isDisplayed=lambda config: False, # Hidden since FTS only has one index type currently +) + +CaseConfigParamInput_FTS_inverted_index_algo = CaseConfigInput( + label=CaseConfigParamType.inverted_index_algo, + displayLabel="Inverted Index Algorithm", + inputHelp="Algorithm for building and querying sparse inverted index.", + inputType=InputType.Option, + inputConfig={ + "value": "DAAT_MAXSCORE", + "options": ["DAAT_MAXSCORE", "DAAT_WAND", "TAAT_NAIVE"], + }, +) + +CaseConfigParamInput_FTS_bm25_k1 = CaseConfigInput( + label=CaseConfigParamType.bm25_k1, + displayLabel="BM25 k1", + inputHelp="BM25 k1 parameter controls term frequency saturation [1.2, 2.0]. Higher values emphasize term frequency.", + inputType=InputType.Float, + inputConfig={ + "value": 1.5, + "step": 0.1, + "min": 1.2, + "max": 2.0, + }, +) + +CaseConfigParamInput_FTS_bm25_b = CaseConfigInput( + label=CaseConfigParamType.bm25_b, + displayLabel="BM25 b", + inputHelp="BM25 b parameter controls document length normalization [0.0, 1.0]. 0.75 is commonly used.", + inputType=InputType.Float, + inputConfig={ + "value": 0.75, + "step": 0.05, + "min": 0.0, + "max": 1.0, + }, +) + +CaseConfigParamInput_FTS_analyzer_tokenizer = CaseConfigInput( + label=CaseConfigParamType.analyzer_tokenizer, + displayLabel="Analyzer Tokenizer", + inputHelp="Text tokenizer type for BM25 analysis.", + inputType=InputType.Option, + inputConfig={ + "value": "standard", + "options": ["standard", "whitespace", "keyword"], + }, +) + +CaseConfigParamInput_FTS_analyzer_enable_lowercase = CaseConfigInput( + label=CaseConfigParamType.analyzer_enable_lowercase, + displayLabel="Enable Lowercase Filter", + inputHelp="Convert text to lowercase during analysis.", + inputType=InputType.Bool, + inputConfig={ + "value": True, + }, +) + +CaseConfigParamInput_FTS_analyzer_max_token_length = CaseConfigInput( + label=CaseConfigParamType.analyzer_max_token_length, + displayLabel="Max Token Length", + inputHelp="Maximum length of individual tokens (optional, leave empty to disable).", + inputType=InputType.Number, + inputConfig={ + "value": None, + "min": 1, + "max": 100, + }, +) + +CaseConfigParamInput_FTS_analyzer_stop_words = CaseConfigInput( + label=CaseConfigParamType.analyzer_stop_words, + displayLabel="Stop Words", + inputHelp="Comma-separated list of stop words to filter out.", + inputType=InputType.Text, + inputConfig={ + "value": "", + "placeholder": "of,to,the,and,or", + }, +) + +CaseConfigParamInput_FTS_drop_ratio_search = CaseConfigInput( + label=CaseConfigParamType.drop_ratio_search, + displayLabel="Drop Ratio (search)", + inputHelp="Search-time sparsification ratio. 0.0 keeps best recall; larger is faster.", + inputType=InputType.Float, + inputConfig={ + "value": 0.0, + "step": 0.1, + "min": 0.0, + "max": 0.99, + }, +) + + MilvusLoadConfig = [ CaseConfigParamInput_IndexType, CaseConfigParamInput_M, @@ -2168,6 +2306,29 @@ class CaseConfigInput(BaseModel): CaseConfigParamInput_Milvus_use_partition_key, ] + +MilvusFtsConfig = [ + CaseConfigParamInput_IndexType_FTS, + CaseConfigParamInput_FTS_inverted_index_algo, + CaseConfigParamInput_FTS_bm25_k1, + CaseConfigParamInput_FTS_bm25_b, + CaseConfigParamInput_FTS_analyzer_tokenizer, + CaseConfigParamInput_FTS_analyzer_enable_lowercase, + CaseConfigParamInput_FTS_analyzer_max_token_length, + CaseConfigParamInput_FTS_analyzer_stop_words, + CaseConfigParamInput_FTS_drop_ratio_search, +] + +ZillizCloudFtsConfig = [ + CaseConfigParamInput_IndexType_FTS, + CaseConfigParamInput_ZillizLevel, +] + +ElasticCloudFtsConfig = [] +VespaFtsConfig = [] +OSSOpenSearchFtsConfig = [] +TurboPufferFtsConfig = [] + WeaviateLoadConfig = [ CaseConfigParamInput_MaxConnections, CaseConfigParamInput_EFConstruction_Weaviate, @@ -2691,9 +2852,10 @@ class FilterType(Enum): inputConfig={ "min": 1, "max": 200, - "value": 10, + "value": 2, }, - isDisplayed=lambda config: config[CaseConfigParamType.IndexType] == IndexType.IVFPQ.value + isDisplayed=lambda config: config[CaseConfigParamType.IndexType] == IndexType.HNSW.value + or config[CaseConfigParamType.IndexType] == IndexType.IVFPQ.value or config[CaseConfigParamType.IndexType] == IndexType.IVFBQ.value, inputHelp="Reorder factor", ) @@ -2847,15 +3009,256 @@ class FilterType(Enum): CaseConfigParamInput_NumberOfRegions_Lindorm, ] +# PolarDB configs +CaseConfigParamInput_IndexType_PolarDB = CaseConfigInput( + label=CaseConfigParamType.IndexType, + inputHelp="Select Index Type", + inputType=InputType.Option, + inputConfig={ + "options": [ + IndexType.HNSW.value, + IndexType.HNSW_PQ.value, + IndexType.HNSW_SQ.value, + ], + }, +) + +CaseConfigParamInput_M_PolarDB = CaseConfigInput( + label=CaseConfigParamType.M, + inputType=InputType.Number, + inputConfig={ + "min": 2, + "max": 1024, + "value": 16, + }, + inputHelp="HNSW M parameter", +) + +CaseConfigParamInput_EFConstruction_PolarDB = CaseConfigInput( + label=CaseConfigParamType.ef_construction, + inputType=InputType.Number, + inputConfig={ + "min": 1, + "max": 8192, + "value": 200, + }, + inputHelp="ef_construction", +) + +CaseConfigParamInput_EFSearch_PolarDB = CaseConfigInput( + label=CaseConfigParamType.ef_search, + inputType=InputType.Number, + inputConfig={ + "min": 1, + "max": 8192, + "value": 200, + }, + inputHelp="ef_search", +) + +CaseConfigParamInput_InsertWorkers_PolarDB = CaseConfigInput( + label=CaseConfigParamType.insert_workers, + inputType=InputType.Number, + inputConfig={ + "min": 1, + "max": 1024, + "value": 10, + }, + inputHelp="Number of insert workers", +) + +CaseConfigParamInput_PostLoadIndex_PolarDB = CaseConfigInput( + label=CaseConfigParamType.post_load_index, + inputType=InputType.Bool, + inputConfig={ + "value": True, + }, + inputHelp="Create index after data load via ALTER TABLE", +) + +CaseConfigParamInput_PQM_PolarDB = CaseConfigInput( + label=CaseConfigParamType.pq_m, + inputType=InputType.Number, + inputConfig={ + "min": 1, + "max": 16383, + "value": 1, + }, + isDisplayed=lambda config: config.get(CaseConfigParamType.IndexType, None) == IndexType.HNSW_PQ.value, + inputHelp="PQ M parameter", +) + +CaseConfigParamInput_PQNbits_PolarDB = CaseConfigInput( + label=CaseConfigParamType.pq_nbits, + inputType=InputType.Number, + inputConfig={ + "min": 1, + "max": 24, + "value": 8, + }, + isDisplayed=lambda config: config.get(CaseConfigParamType.IndexType, None) == IndexType.HNSW_PQ.value, + inputHelp="PQ nbits parameter", +) + +CaseConfigParamInput_SQType_PolarDB = CaseConfigInput( + label=CaseConfigParamType.sq_type, + inputType=InputType.Option, + inputConfig={ + "options": [ + "8bit", + "4bit", + "8bit_uniform", + "4bit_uniform", + "8bit_direct", + "8bit_direct_signed", + "fp16", + "bf16", + "6bit", + ], + }, + isDisplayed=lambda config: config.get(CaseConfigParamType.IndexType, None) == IndexType.HNSW_SQ.value, + inputHelp="Scalar quantizer type", +) + +PolarDBConfig = [ + CaseConfigParamInput_IndexType_PolarDB, + CaseConfigParamInput_M_PolarDB, + CaseConfigParamInput_EFConstruction_PolarDB, + CaseConfigParamInput_EFSearch_PolarDB, + CaseConfigParamInput_InsertWorkers_PolarDB, + CaseConfigParamInput_PostLoadIndex_PolarDB, + CaseConfigParamInput_PQM_PolarDB, + CaseConfigParamInput_PQNbits_PolarDB, + CaseConfigParamInput_SQType_PolarDB, +] + +# ADBPG (Aliyun AnalyticDB for PostgreSQL) configs +CaseConfigParamInput_Algorithm_Adbpg = CaseConfigInput( + label=CaseConfigParamType.algorithm, + inputHelp="Select Nova algorithm variant", + inputType=InputType.Option, + inputConfig={ + "options": ["novamr", "novad"], + }, +) + +CaseConfigParamInput_HnswM_Adbpg = CaseConfigInput( + label=CaseConfigParamType.hnsw_m, + inputType=InputType.Number, + inputConfig={ + "min": 1, + "max": 256, + "value": 16, + }, + inputHelp="HNSW M parameter", +) + +CaseConfigParamInput_EFConstruction_Adbpg = CaseConfigInput( + label=CaseConfigParamType.ef_construction, + inputType=InputType.Number, + inputConfig={ + "min": 1, + "max": 2000, + "value": 200, + }, + inputHelp="HNSW ef_construction", +) + +CaseConfigParamInput_RabitqBits_Adbpg = CaseConfigInput( + label=CaseConfigParamType.rabitq_bits, + inputType=InputType.Number, + inputConfig={ + "min": 1, + "max": 8, + "value": 7, + }, + inputHelp="RaBitQ quantization bits", +) + +CaseConfigParamInput_AutoReduction_Adbpg = CaseConfigInput( + label=CaseConfigParamType.auto_reduction, + inputType=InputType.Bool, + inputConfig={"value": False}, + inputHelp="Enable auto_reduction=on for index build", +) + +CaseConfigParamInput_EFSearch_Adbpg = CaseConfigInput( + label=CaseConfigParamType.ef_search, + inputType=InputType.Number, + inputConfig={ + "min": 1, + "max": 2000, + "value": 100, + }, + inputHelp="fastann.hnsw_ef_search", +) + +CaseConfigParamInput_MaxScanPoints_Adbpg = CaseConfigInput( + label=CaseConfigParamType.max_scan_points, + inputType=InputType.Number, + inputConfig={ + "min": 1, + "max": 100000, + "value": 2000, + }, + inputHelp="fastann.hnsw_max_scan_points", +) + +CaseConfigParamInput_QuantizeRescoreAmp_Adbpg = CaseConfigInput( + label=CaseConfigParamType.quantize_rescore_amp, + inputType=InputType.Float, + inputConfig={ + "min": 0.0, + "max": 100.0, + "value": 1.0, + "step": 0.1, + }, + inputHelp="fastann.quantize_rescore_amp", +) + +CaseConfigParamInput_NovaAdaptiveGamma_Adbpg = CaseConfigInput( + label=CaseConfigParamType.nova_adaptive_gamma, + inputType=InputType.Float, + inputConfig={ + "min": 0.0, + "max": 1.0, + "value": 0.0, + "step": 0.05, + }, + inputHelp="fastann.nova_adaptive_gamma", +) + +AdbpgLoadConfig = [ + CaseConfigParamInput_Algorithm_Adbpg, + CaseConfigParamInput_HnswM_Adbpg, + CaseConfigParamInput_EFConstruction_Adbpg, + CaseConfigParamInput_RabitqBits_Adbpg, + CaseConfigParamInput_AutoReduction_Adbpg, +] + +AdbpgPerformanceConfig = [ + CaseConfigParamInput_Algorithm_Adbpg, + CaseConfigParamInput_HnswM_Adbpg, + CaseConfigParamInput_EFConstruction_Adbpg, + CaseConfigParamInput_RabitqBits_Adbpg, + CaseConfigParamInput_AutoReduction_Adbpg, + CaseConfigParamInput_EFSearch_Adbpg, + CaseConfigParamInput_MaxScanPoints_Adbpg, + CaseConfigParamInput_QuantizeRescoreAmp_Adbpg, + CaseConfigParamInput_NovaAdaptiveGamma_Adbpg, +] + # Map DB to config CASE_CONFIG_MAP = { DB.Milvus: { CaseLabel.Load: MilvusLoadConfig, CaseLabel.Performance: MilvusPerformanceConfig, CaseLabel.Streaming: MilvusPerformanceConfig, + CaseLabel.FullTextSearchPerformance: MilvusFtsConfig, }, DB.ZillizCloud: { CaseLabel.Performance: ZillizCloudPerformanceConfig, + CaseLabel.FullTextSearchPerformance: ZillizCloudFtsConfig, }, DB.WeaviateCloud: { CaseLabel.Load: WeaviateLoadConfig, @@ -2864,6 +3267,7 @@ class FilterType(Enum): DB.ElasticCloud: { CaseLabel.Load: ESLoadingConfig, CaseLabel.Performance: ESPerformanceConfig, + CaseLabel.FullTextSearchPerformance: ElasticCloudFtsConfig, }, DB.AWSOpenSearch: { CaseLabel.Load: AWSOpensearchLoadingConfig, @@ -2872,6 +3276,7 @@ class FilterType(Enum): DB.OSSOpenSearch: { CaseLabel.Load: OSSOpensearchLoadingConfig, CaseLabel.Performance: OSSOpenSearchPerformanceConfig, + CaseLabel.FullTextSearchPerformance: OSSOpenSearchFtsConfig, }, DB.PgVector: { CaseLabel.Load: PgVectorLoadingConfig, @@ -2912,6 +3317,7 @@ class FilterType(Enum): DB.Vespa: { CaseLabel.Load: VespaLoadingConfig, CaseLabel.Performance: VespaPerformanceConfig, + CaseLabel.FullTextSearchPerformance: VespaFtsConfig, }, DB.LanceDB: { CaseLabel.Load: LanceDBLoadConfig, @@ -2937,14 +3343,26 @@ class FilterType(Enum): CaseLabel.Load: LindormLoadConfig, CaseLabel.Performance: LindormPerformanceConfig, }, + DB.PolarDB: { + CaseLabel.Load: PolarDBConfig, + CaseLabel.Performance: PolarDBConfig, + }, + DB.TurboPuffer: { + CaseLabel.FullTextSearchPerformance: TurboPufferFtsConfig, + }, + DB.Adbpg: { + CaseLabel.Load: AdbpgLoadConfig, + CaseLabel.Performance: AdbpgPerformanceConfig, + }, } def get_case_config_inputs(db: DB, case_label: CaseLabel) -> list[CaseConfigInput]: - if db not in CASE_CONFIG_MAP: - return [] + db_case_config_map = CASE_CONFIG_MAP.get(db, {}) if case_label == CaseLabel.Load: - return CASE_CONFIG_MAP[db][CaseLabel.Load] - elif case_label == CaseLabel.Performance or case_label == CaseLabel.Streaming: - return CASE_CONFIG_MAP[db][CaseLabel.Performance] + return db_case_config_map.get(CaseLabel.Load, []) + if case_label == CaseLabel.Performance or case_label == CaseLabel.Streaming: + return db_case_config_map.get(CaseLabel.Performance, []) + elif case_label == CaseLabel.FullTextSearchPerformance: + return db_case_config_map.get(CaseLabel.FullTextSearchPerformance, []) return [] diff --git a/vectordb_bench/frontend/config/styles.py b/vectordb_bench/frontend/config/styles.py index fabe9bdcf..4b162e9ed 100644 --- a/vectordb_bench/frontend/config/styles.py +++ b/vectordb_bench/frontend/config/styles.py @@ -74,6 +74,8 @@ def getPatternShape(i): DB.Zvec: "https://zvec.org/img/zvec-logo-light.svg", DB.Endee: "data:image/svg+xml,%3c?xml%20version=%271.0%27%20encoding=%27UTF-8%27?%3e%3csvg%20id=%27Layer_1%27%20xmlns=%27http://www.w3.org/2000/svg%27%20version=%271.1%27%20viewBox=%270%200%20600%20600%27%3e%3c!--%20Generator:%20Adobe%20Illustrator%2030.0.0,%20SVG%20Export%20Plug-In%20.%20SVG%20Version:%202.1.1%20Build%20123)%20--%3e%3cdefs%3e%3cstyle%3e%20.st0%20{%20fill:%20%233266a4;%20}%20%3c/style%3e%3c/defs%3e%3cpath%20class=%27st0%27%20d=%27M106.22,490.02H10.36l-.04-184.85c11.19-163.31,232.1-211.74,306.42-61.94,23.96,48.3,15.59,99.83,17,152.01h61.62c15.25,0,42.7-13.75,54.75-23.2,66.11-51.86,57.28-158.2-16.28-198.49-9.65-5.28-33.16-14.19-43.74-14.19h-154.31v-91.09c0-.87,2.55-1.86,3.63-1.63,104.05,4.23,201.15-21.64,284.48,55.84,119.18,110.8,69.12,325.47-91.33,362.6-28.65,6.63-85.47,7.76-115.02,4.8-19.71-1.97-43.29-16.57-55.97-31.45-37.98-44.56-20.77-98.07-24.7-151.16-5.18-69.99-100-85.31-125.9-20.47-1.16,2.92-4.76,13.88-4.76,16.3v186.91Z%27/%3e%3c/svg%3e", DB.Lindorm: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJgAAACKCAYAAABW3IOxAAAMT2lDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnltSIQQIREBK6E0QqQGkhNACSC+CqIQkQCgxJgQVO7K4gmsXESwrugqi2FZAFhvqqiuLgr0uFlSUdXFd7MqbEECXfeV7831z57//nPnnnHPn3rkDAL2LL5XmopoA5EnyZbEhAazJySksUg8gAmNABjZAiy+QSznR0REAluH27+X1NYAo28sOSq1/9v/XoiUUyQUAINEQpwvlgjyIfwQAbxFIZfkAEKWQN5+VL1XidRDryKCDENcocaYKtyhxugpfGrSJj+VC/AgAsjqfL8sEQKMP8qwCQSbUocNogZNEKJZA7A+xb17eDCHEiyC2gTZwTrpSn53+lU7m3zTTRzT5/MwRrIplsJADxXJpLn/O/5mO/13ychXDc1jDqp4lC41Vxgzz9ihnRrgSq0P8VpIeGQWxNgAoLhYO2isxM0sRmqCyR20Eci7MGWBCPFGeG8cb4mOF/MBwiA0hzpDkRkYM2RRliIOVNjB/aIU4nxcPsR7ENSJ5UNyQzQnZjNjhea9lyLicIf4pXzbog1L/syIngaPSx7SzRLwhfcyxMCs+CWIqxIEF4sRIiDUgjpTnxIUP2aQWZnEjh21kilhlLBYQy0SSkACVPlaeIQuOHbLfnScfjh07kSXmRQ7hzvys+FBVrrBHAv6g/zAWrE8k4SQM64jkkyOGYxGKAoNUseNkkSQhTsXjetL8gFjVWNxOmhs9ZI8HiHJDlLwZxPHygrjhsQX5cHGq9PESaX50vMpPvDKbHxat8gffDyIAFwQCFlDAmg5mgGwgbu9t7IV3qp5gwAcykAlEwGGIGR6RNNgjgdc4UAh+h0gE5CPjAgZ7RaAA8p9GsUpOPMKprg4gY6hPqZIDHkOcB8JBLrxXDCpJRjxIBI8gI/6HR3xYBTCGXFiV/f+eH2a/MBzIRAwxiuEZWfRhS2IQMZAYSgwm2uIGuC/ujUfAqz+szjgb9xyO44s94TGhg/CAcJXQRbg5XVwkG+XlJNAF9YOH8pP+dX5wK6jphgfgPlAdKuNM3AA44K5wHg7uB2d2gyx3yG9lVlijtP8WwVdPaMiO4kRBKWMo/hSb0SM17DTcRlSUuf46Pypf00fyzR3pGT0/96vsC2EbPtoS+xY7hJ3FTmLnsRasEbCw41gT1oYdVeKRFfdocMUNzxY76E8O1Bm9Zr48WWUm5U51Tj1OH1V9+aLZ+cqXkTtDOkcmzszKZ3HgjiFi8SQCx3EsZydnNwCU+4/q8/YqZnBfQZhtX7glvwHgc3xgYOCnL1zYcQAOeMBPwpEvnA0bbi1qAJw7IlDIClQcrrwQ4JeDDt8+fbi/mcP9zQE4A3fgDfxBEAgDUSAeJINp0PssuM5lYBaYBxaDElAGVoH1oBJsBdtBDdgLDoJG0AJOgp/BBXAJXAW34erpBs9BH3gNPiAIQkJoCAPRR0wQS8QecUbYiC8ShEQgsUgykoZkIhJEgcxDliBlyBqkEtmG1CIHkCPISeQ80oHcRO4jPcifyHsUQ9VRHdQItULHo2yUg4aj8ehUNBOdiRaixegKtAKtRvegDehJ9AJ6Fe1Cn6P9GMDUMCZmijlgbIyLRWEpWAYmwxZgpVg5Vo3VY83wOV/GurBe7B1OxBk4C3eAKzgUT8AF+Ex8Ab4cr8Rr8Ab8NH4Zv4/34Z8JNIIhwZ7gReARJhMyCbMIJYRywk7CYcIZ+C51E14TiUQm0ZroAd/FZGI2cS5xOXEzcR/xBLGD+JDYTyKR9En2JB9SFIlPyieVkDaS9pCOkzpJ3aS3ZDWyCdmZHExOIUvIReRy8m7yMXIn+Qn5A0WTYknxokRRhJQ5lJWUHZRmykVKN+UDVYtqTfWhxlOzqYupFdR66hnqHeorNTU1MzVPtRg1sdoitQq1/Wrn1O6rvVPXVrdT56qnqivUV6jvUj+hflP9FY1Gs6L501Jo+bQVtFraKdo92lsNhoajBk9DqLFQo0qjQaNT4wWdQrekc+jT6IX0cvoh+kV6ryZF00qTq8nXXKBZpXlE87pmvxZDa4JWlFae1nKt3VrntZ5qk7SttIO0hdrF2tu1T2k/ZGAMcwaXIWAsYexgnGF06xB1rHV4Otk6ZTp7ddp1+nS1dV11E3Vn61bpHtXtYmJMKyaPmctcyTzIvMZ8P8ZoDGeMaMyyMfVjOse80Rur568n0ivV26d3Ve+9Pks/SD9Hf7V+o/5dA9zAziDGYJbBFoMzBr1jdcZ6jxWMLR17cOwtQ9TQzjDWcK7hdsM2w34jY6MQI6nRRqNTRr3GTGN/42zjdcbHjHtMGCa+JmKTdSbHTZ6xdFkcVi6rgnWa1WdqaBpqqjDdZtpu+sHM2izBrMhsn9ldc6o52zzDfJ15q3mfhYnFJIt5FnUWtywplmzLLMsNlmct31hZWyVZLbVqtHpqrWfNsy60rrO+Y0Oz8bOZaVNtc8WWaMu2zbHdbHvJDrVzs8uyq7K7aI/au9uL7Tfbd4wjjPMcJxlXPe66g7oDx6HAoc7hviPTMcKxyLHR8cV4i/Ep41ePPzv+s5ObU67TDqfbE7QnhE0omtA84U9nO2eBc5XzFReaS7DLQpcml5eu9q4i1y2uN9wYbpPclrq1un1y93CXude793hYeKR5bPK4ztZhR7OXs895EjwDPBd6tni+83L3yvc66PWHt4N3jvdu76cTrSeKJu6Y+NDHzIfvs82ny5flm+b7vW+Xn6kf36/a74G/ub/Qf6f/E44tJ5uzh/MiwClAFnA44A3XizufeyIQCwwJLA1sD9IOSgiqDLoXbBacGVwX3BfiFjI35EQoITQ8dHXodZ4RT8Cr5fWFeYTNDzsdrh4eF14Z/iDCLkIW0TwJnRQ2ae2kO5GWkZLIxigQxYtaG3U32jp6ZvRPMcSY6JiqmMexE2LnxZ6NY8RNj9sd9zo+IH5l/O0EmwRFQmsiPTE1sTbxTVJg0pqkrsnjJ8+ffCHZIFmc3JRCSklM2ZnSPyVoyvop3aluqSWp16ZaT5099fw0g2m5045Op0/nTz+URkhLStud9pEfxa/m96fz0jel9wm4gg2C50J/4Tphj8hHtEb0JMMnY03G00yfzLWZPVl+WeVZvWKuuFL8Mjs0e2v2m5yonF05A7lJufvyyHlpeUck2pIcyekZxjNmz+iQ2ktLpF0zvWaun9knC5ftlCPyqfKmfB34o9+msFF8o7hf4FtQVfB2VuKsQ7O1Zktmt82xm7NszpPC4MIf5uJzBXNb55nOWzzv/nzO/G0LkAXpC1oXmi8sXti9KGRRzWLq4pzFvxY5Fa0p+mtJ0pLmYqPiRcUPvwn5pq5Eo0RWcn2p99Kt3+Lfir9tX+aybOOyz6XC0l/KnMrKyz4uFyz/5bsJ31V8N7AiY0X7SveVW1YRV0lWXVvtt7pmjdaawjUP105a27COta503V/rp68/X+5avnUDdYNiQ1dFREXTRouNqzZ+rMyqvFoVULVvk+GmZZvebBZu7tziv6V+q9HWsq3vvxd/f2NbyLaGaqvq8u3E7QXbH+9I3HH2B/YPtTsNdpbt/LRLsqurJrbmdK1Hbe1uw90r69A6RV3PntQ9l/YG7m2qd6jfto+5r2w/2K/Y/+xA2oFrB8MPth5iH6r/0fLHTYcZh0sbkIY5DX2NWY1dTclNHUfCjrQ2ezcf/snxp10tpi1VR3WPrjxGPVZ8bOB44fH+E9ITvSczTz5snd56+9TkU1dOx5xuPxN+5tzPwT+fOss5e/ycz7mW817nj/zC/qXxgvuFhja3tsO/uv16uN29veGix8WmS56Xmjsmdhzr9Os8eTnw8s9XeFcuXI282nEt4dqN66nXu24Ibzy9mXvz5a2CWx9uL7pDuFN6V/Nu+T3De9W/2f62r8u96+j9wPttD+Ie3H4oePj8kfzRx+7ix7TH5U9MntQ+dX7a0hPcc+nZlGfdz6XPP/SW/K71+6YXNi9+/MP/j7a+yX3dL2UvB/5c/kr/1a6/XP9q7Y/uv/c67/WHN6Vv9d/WvGO/O/s+6f2TD7M+kj5WfLL91Pw5/POdgbyBASlfxh/8FcCA8miTAcCfuwCgJQPAgOdG6hTV+XCwIKoz7SAC/wmrzpCDxR2AevhPH9ML/26uA7B/BwBWUJ+eCkA0DYB4T4C6uIzU4bPc4LlTWYjwbPD9tE/peeng3xTVmfQrv0e3QKnqCka3/wLmpoMnuLWGFQAAAIplWElmTU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAIdpAAQAAAABAAAATgAAAAAAAACQAAAAAQAAAJAAAAABAAOShgAHAAAAEgAAAHigAgAEAAAAAQAAAJigAwAEAAAAAQAAAIoAAAAAQVNDSUkAAABTY3JlZW5zaG90rCu3yAAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAAdZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDYuMC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFlEaW1lbnNpb24+MTM4PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjE1MjwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlVzZXJDb21tZW50PlNjcmVlbnNob3Q8L2V4aWY6VXNlckNvbW1lbnQ+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpf6GgjAAAAHGlET1QAAAACAAAAAAAAAEUAAAAoAAAARQAAAEUAACFD9bmq1QAAIQ9JREFUeAGcncu2JFdxhk+LBQJzsw1mgO0ZSIwl8RDm5ocw6pY9smH5LUD2DHXDU3DxQ4AaewYC7JFnXJbBmJuE2vFF5rfPX3Eyz2nYUtXeEfHHH7FzR+3MyqpTfe/tt99+clXt3r17V0+ePLno0duw0cCVT/fasseeTT90jJ955pk03znWP3kzfuqNISm29Gc88WKzT5/UOyb+XfM4iiWvPGe5pK8+ic35n3HJkf6Oz3zUZ6/PWXz1E4eM7V4lu1VOsaLAwMMDKJB+NvH22pFp00e9uLPemNjnYibnEV8e/OQ5wh7Fx4cH8zdW+h7pkgc7eHoeHseJUU5udNPvrvkbTz5leeS3N4b41KuTA/mu+OnjGM4VvwZdObkwAukzgR26dMriJT7zm3r95El/bfZpMyf9znj1fRrc2fzlmPHh5GEu5pCxjnTixck7uYxrL05O+vTJsT639TM+2Iwxfc1bPf48jl5AYujvFagLbO/axlhCe+30Sao+SZv4KXcx/TMOOuXJexb/NnxyPA3uaeObe/LfNTb+bb5PGz9jySd/2o7G4rXph/4oftqPxvLMfp0icZLYfoLV2087sjsBfI7RTxnd5EH21OQkwM02/bCjs+lL/NteDMbTXznznpxgxGnLXt85X2Xzkcdc6dGJS7382ulnEy8G+5z/lBPLmAf5OQc45GVsA2fTfqRrnnpaaME6Z58JpH6OM9C03SbjR/y7cjiz628M85h49eK0e1CR5Uqs+umnbJ8+6rI33hEubcZLHT5ZoPBOnLHkT3/xYrI/m7/+YOFUPuLXJpb+3u9///vCXieOkgYYvUQ5MW3g0u5YPTgSp+mfSbRhPMlhjMRjU04cFMg8jCPW+PoZbsrypV1O+ne84x1Xr//gN1eP3/jd1YPPfmDNO3mSgzE2F8685NeesmN6fM8w2sTpl/GnTYz95FZvj51H5i3/UfyJd95dYDjYACKrqwLsIMrgkswFNBGTkEc5OWcsZfqV2J7TEY84YyJnHGViwocNLHLi5BZvn7ky/+/+6M2rr37zf6vAfntVR+bq/mfeT8CrB5/7IC4XTX65nU/migN2HuqnHzmIO8o7fRu4P8mDKEfaHYM74kBnvNnLeeSLTju9814X+Sgld0x/V3MS6Tt9ZnDt0yfl6XOXvCa0L0zGkNcDhi3zVoZjLvhXv/XLq4ff/EUfGznFP/jMB7ZiK8WMb75g/5j4+NGSZ9McPxtDa85P2x8yf3nsZx7JD8YY4rXfWmBHThlIEklv6/VzktnrJyblXPCMN3ObcnI4tgebvOjhzgLjdHj/Sz++urffGG7+2r3qdd80nQvD2mgotJc//b5VtADAn7Xb4jtH+skxdcqzJy462uRQd9f8xSWPcbAd6Y/s/S5SQzpBQlO3SduzSU8/rL6SwWA/wugv520YbROLjM14jGnKM34b60lc5mAMek6Dng7bZy8i/amvLjIKiJh7jy+F9vlPvXfFyFiO4TG3HKdOPT1+t9mcr/zK+qiHKxt2mnb8LLrEMRbL2HwcGw/ZJob+4jYFgCRLh7SZlHZtEiPnGE6TP0pIPL3NPPSl59FJs7B7E4eYMXOcfo7TD9/v/uh3V4++sV1nIa/WC1HxCEkO1XEdxsgebGdUed3/9PuvXnz+XVcvPfdu1J0zseb8yU87vXIr68n80lddYtXJoez8sweDXZ0+6lNmTLPw8NP3KP6RDf8+RWqchA2IxUSmgbfPYOhMXk7ldgj79MMub/qc4RIjN736s/hgtIl/+I1fXD2qi/iukp7bXlBiMWz1UM6lXOMS5vHZ7fc/9b6r+/Vu03yIZSO+BafO3gVVtj86NnMeR9ipQ57xk2fG10ZPy/VQd6TXr99FAnDC6ZSOOqATc9vBEwPehs44qXNM7wT0T/lsnP7G0P/MBpfXWR13v9ZKfI/ruG671dXVS8+/++rF557ti35tq9h03At02+u267N+11n2zC3nguuZLK07PzjnNsdgZ4Hoj89RfPA01yW5py/xzDNtjNUbf/G89dZbTzAaQMeckDp69YwlZSwhY1rakLWjZzztYGzYxaSf9uTgNgKy+U+8XNrh+M4bv67rrF9utx3K96LtO9ATDnzZXICv/fNHrl78+LMN5Z3la9/4edXW9c4247rTPan72G8/efvq7z/3p/2OM3PPuOmfY+ObP7bkYP407diygdcHvb5iJn/aGWPHX365jDP95bW/2MF01jhl9OiyGUgsAdGpF6vf1GunnxjkqdNfvb0HYMbPA/D4h7+9esh11hu/WWHlawVzY4H2KVb0q5c51dV9L/nFE5fT6qNv1am1GnIXHM6xyMTHhj9vAl58/tm6PtsKtR33JzDNMY5d5j/xyPjQyIuHHK2sp4wvjl6/5NfXOYKb/uhoYtIfvRyMaes2hQYDb+ZLIieBbeLE2x9NVpuxlOmnTn4nknZt+KU9ZcbpQzH0rsPisyjd19Bqgqv+U/5knQ7vf7Yu2GvXgkeuo3hyE9PWxdahLLwtFv4P6o3Ay1W0cuEjPz0tbcgsJDoeYtHb9FMWhyyXGHpfMOrEKeuDHh0PdKnHZtNPWdx6F4kBpcBJqF5HZQnF26tPvGNt9MmT9jygZ3j0+ohXJ6/vDr/z/V8D3vCs4X4m6VPh5tQF9uJz71qFBbc8QNgBv/uDN3tHU298TrvYHtapc8UpH3E1uNbXkELOG7VbCpfx5Ma2eBCqpQ3Z+YObNuypTzt+NHQ8Ms6UxbXDeJrxjXFxHwxyK1t/A85gEpzhEp9j8fo7cXrHYNKunHZ50GlP3eMfctvhF+vjHRY0W/LXoa2AdUFeOwvv/Gjy6rOuu2oRLIzMR/zC7ZyNqfGMLy/9K5/9YN+oTb7MDwxy2tHRjLtJlzht+oqhl18dWB5z/bUf9fJjSz70xryxg02iJJFIgsQm7sgudgWuA0ZLP23oTTjtqWdM0y6ed4bcdnj8g99tgPFMMbHY4vGnYPgAOxt6MP1O88s/WSaLcfqZB0DGX/u3/zv+iOmk2Lw+e/Hj7+pYmd8KXgP16jJu6iZOG3p8pv1Ip89RP+PKhz65btwHk0wCE0KPbla4OP0MpHzWZxJzjI882HgQl96WdnXzohu9uN7A9tNi44uL2w5cC7GoC7eTcTpcN17JAX31+dERKvwoDj4qUqY3V3Lis8xZ2HCVcwEbXU81rv+5UettDSw0uIjjcVDXxv3J/I2rnBh50HlKA8f4bF2Ni89R/BnH+OBp6yIfIRMQKMGUwdNMVF/xm/X6+cz/GrHFV5Zn+hlHHPG5znLXyh0GjDw1uc2lDij3sh7sF/Dy0MPtNRsfGWW7yKOoehekIqiQ4swbq/iJZ0yREb8LjfttpLK7XvZ7IZfZ0zD+2ZyPx8HjD2ba0g8czULS3x7bHKNLzilj08ceDGPbKrALZTnO+0s6SopscMnEpE3eI+wRXq55QNSnTxfW17frrLQzvhFvLwqud+YOIWfuNr3wbYCsHrv/2omw1XGyOU8Lw/jqkeXXJ/vEEYt7Z+j+4W//bH2QjizvXB/95VS2oNCnP8cXOe36GkM5/dTN+2/q9XX91ikSAEYM2esggf2RnkRMBjtjW+LFYNPHiWpbCe6LqL+cFNd9ro32he84xiufxiPv/p56zEs+/LjO4nTIdduTWlh8ln3nvJ5JRwp7yVVnnVfE98J9Q2/H1jFvPh6yq1mf5Qd/776MKv4qgBo//OKH12eb5uXxyePmsQGjnS9K0tLmmB6svX70PGjyGKeVQz/55GneIug7+YIkmL0BTeYIL2b66pP69Mcv5cQdjcHfuPjmgGyrdFF03M96+TPvW3fhzZF4XGf1Hf03fluulyUkbsb3Dr96cT1HKoZ1KSr4uKHa13nxVZ7G7fNlR+sbtTsezuRTdldEZsHFaKe3JT86sOrE3NaDpWWMxBt/2o2RffPUVtcFJomOEFG1ZwHV2+tHbxA57RPreGLlmfHl0L4KzAPCtY2tdFxnccsh75rnweGabbv4Lqc9Z9y3XYQRB3ovGMS9mbcy9UTblmVfmIq/eW/+Fgi+PPK48tUg4nMTmGZ8dtJ792pORaE/9hkfHc3jYgxkdRvi2jf14FNO7Jl+xtPHXk781ykS4Sj5qTeoJJDmOIMw1l8/dBnnTA/OBt4tWt8+RX5pu32Quw98uSBy2Hdh8TmiBbnXUdsrTiUs9LovvQWksvNWWS7mpd2egsHbvHi3mfMBh9zfnN1v0uprn/fn1M1+xs/jeoQ9ssOhXj5kx/Ck3TF6MalrvDuYBoFtjIOtHhxjHvpMLLJ4x2ATr789OFr6GSv9wLATUWAPXv3pwudCPn7tL4GtBqenw+/U55Bg+X+rmpOiwpsC2nGWWMfp3QUOjHsrrBhVq/dY7ccu32jkrvrg1Z/0jeFrvxpViPmC8RjN46Ie/7SpT50xsD2NPjkY65e+6uTuPGqCpb8Okg4AsNmmTb198ug3faYemUfuUOkjnj713Km//+UfG3rrybUWMQuMQlz3sy7RXUBdFHDvO1rHsQALb/wupb1AoFlFunM2Lnh29Wm3fTFx++DbGBZYcmOjwPxKdh4DbMqMeeRxPA1eBv3AJI8+6GZLn2lDPuK5uJMPaJIYaOrB3tZu8zMRMfDIr03uxKijZ0d68OXawbZtqE29u5ScBQbvxe0BjpsbD4vSniP+UYEVD818Ot/g6rzDb6fdusB1wKLi5i5vPj75/Hsag/9tBeatlduOkzYIzZMxemXH2YOZTbz65Fb3NP26BgMsKWSTMG1s675SDKKdHl8wNHDKcopF5qFP6vVPDHzi120KlLWAq1Qq/uOHf9Xx4TM+MD8nrID7Ou/zjIIDV0Eu8rco2kYs7lG1sOVDYVu0vsvsuezx4SMmvYX1wse2Tw/Iz3nff/XH9YF5fcQ14h+dIjkONHwdT1lej5k9+lw/ORLf5Af86F0bxnDKi0xDpjWv12CtiSdBAsO0CFJncupIAg4noh2dttRlPDjAYHcBtNuzg/V9sD2gXIjsYB4E/PXBxm722tf/p7hLqOPwDAdjPyDYbenf4B1DHAqKG6E0ue3Nwzn2/PdYj+p+Fl//0SZWX3YwTv20jH/bRf7kaOc7nuB2XczFHKYr/DzEMwZrftPvhlwOvhgXtyQophmCqQMncdrUyZO8acOeTY7EpI7xLLD09xSZ/tjlYNz3oKrY3PkoGsb2YHKMTENXTEy4ZTmPYunvDgRWnH369ymy7sl1CNgrDDnpjwq/LJD0x24zVtrViaFPu/rE/TF2eejXR0UqIeRhxTqZGQiZydrr7ysCOXkSl2P97LHpR08zjmN67oN5DeZCoi/nPkVuwydX//6fb/af+3sNo56eltdni6fnVqeuXmHmuY8Lb2G0814AXXRbvXX8AvUc+G4Z39Lwq9b6Mi9O8fwMQX5Ifp8djM9Ae97srDWs3eaV+rq1uI4becAlLzZlesfoE4NsA0PTrt9cf/GzzxjYUu5xPXUECyMDJRn6HdrJMOZhIomdY/3QT355wTA+sssnD5jeweoPY7fDsyEsEHcwtF535a0B9JPr9e/V13z2rz9jt5lP11o9tbwXVpGsd5+NJ5kqCm7u+unB8peweosa3MMvfLhzIZ9X/uWnl7cpdh92MH8PQz7wPDz+jLHRZxOv7sx+tv7pb4zkSrv67NcOZuCjJNMB3JxU2udYXvUmNPVpT9vEG59vO3BKSawcWWAupjyebvRTjyy2SHklNF3aUezatrHDaW9FPXmdNfmxL/4dTIG99k8f2qW63+V9sIiP0ZwFGpMYOdaevXZ15qWsXT2yYzDTnjo5busvblNATPHQT2IDZw+xyST+aCw2bcYzQeN6WtaHfvpx6uMi3/hyUAC8i6TB03fI/T4WF+Z74bBonHZ8sYA3fn8YHX/Mgc3TYMfj+NRxohnfHTJ3AvnYbfuPTepr2/rhyyn00Rf+gmE33kVy2ixWNsxuzNsCMxa9eavb4cuHQR5HZPNxnDJxlKcf+Gy3xZQDfI+LrOeiwYWUMMkYOzHti2hfuNTfNpbXeMaffIlzDGbdpqjsXQ6LgB1M7Nw1MicW7qVPPNvXSBlfDL7rM0LmV/PPhQfXn3nW/az8S270zIvC8iav+ThfMJ4iGWPvU6QX+fu8wPtiyByT54j7SEccm3ZlejkzTtpTrz8+qU98c5bRY9Y2RR11eJrgM5A+cMgrn6+SxCQOPA8LeuL4Iwt2sAt94dkhLDBsFthRfAry3jPbAvqVaXHG/4//eqt3FT7D3M+PVPTVCx9/Z/9xiDdKM3fGD7/+876mMz/75OebFo/qGoyGnVMkH2XROD51AK6eqa/bUGDzGqzthfP44AN3xmFsPPXgsk17ysmXPjmeeG3kR8x1DXbDUEaahaCdPknVq1Omz0mZrDh6Hn5fST8Ts0efB1Hct7//q6tXXv3Z9aLvBmKeFRgF5R7UebAg3Cejcup/FtIfL8Fu/vQWKsXFYv/d3/xJ5yXGvPxu2be/9ysOwMq9cfuuBBZ+TpFf/eJHVpwusP2vnzo+SVXAfBdpPL7wx3FRNr69+dPTjnBHNo+7eHnktVc/OZTJD451DYagkyTZ65iBsacsx9TLK/aMVxz2u+J5H2zhtqDtR4EZy3ebpWCtupCAXox3mTv0L33iPX3qytsajb/jicLyr8WbLhe2xlvorZCxH31t+/oarADUVjXmR+Gbj/ParNfHCflpjv86Xvt6y4NvbiaJA3Mm648dDvNb+CLtEs/kcFJOB8fYJWScbRGzoNWUxRzxYjvS4+vuNeNthVMX+fWfOxCriJwFZty1A6Eo3l7BfREbg28VmBfhXvfknBt38HR9/2qjBeK89U/57M58F1j+NVTlSaYWmFzw2+ZxyThglMWr47imDW53L7HYz44/Nnxmn76MewdLIEqdGJ81MSbl5JXxU8cYPC11yPLYo6Mlftqwcw3G13W6RvZibr869/MuUp85Ny/czaNxxbK+4AdJFJ6Li9pmboff4WKe5LP68mpx+4br/BKknBy3V/71Z3W9d/ntWl487F5eI4p3fh7vi/nEwou/q4fnrJiOfI1vL0Z55VWDXvlMEHDKR+MkdAyOADR9GKfOBNDT0rZptmf19EcT907+KoZ9QcG7gx1xMHHegfZfYZ/8NGbm0YVSQVhg76bzeWH/SJ3v+HDY4y9fDsNeqP3X4lUkvNMkpzw24tGv+2Aq6UvPNRhFduTrHIEe8W4U1zGPOMTQ2+RKfI7F2R/ZWldPtE6Onia5zvRHNv2m/QwrX/oRSzn9HOMjxjE9BZY/cYmuW03h9dc+ujgpBm4HyCcXfZ82fXdYftvpFpaqjL04OrcSsLGbwcePp+BfpCS3xfWZQ4hqt7kDHsXnNO9vX8DH99s27uuPpqD1Hhtjmlyd2x6/89nMCzN1+k6/lHMnC7obQ3zSL2Olbf26jgD7ybi2vHlAdyCkNHp3nN209NroiUPPg2Zc+9SLw6b9xg7WLBWfHerRXy9eTmN8vvcC378aN1ZxgZvd6LXazWjyt8BTpUdxFbCLiVMWcuNIfdRX40r5Uv2KDh8D2ZwDx4bC4sYrrl/5xz9vCHxHO5inSHYwc5vHBoK0OW7ieMIPW/ZyuWbKk0Mf6ZQn3gJd+lJwmFYzARQ5XoBdPwtuEdYEaMg8SJw+E9amHXza9c/42uXyIt8FRm/zFInsxT12dhNOdeRuk5f+ckfbCkqc/D27fY5ddzxFY7fkV3m88YpJX8YP6vPGx9zrKo6jj4r6Jw8Iwnz2OOTt6RkOc2Yst7opg6GlnvkrW1gbauPGNtdXOz2xpl0+ccrrNgUGlDibrDqd6LW5SCaor72T0K6fXPqrp9dXDD06mrgW6qkLLH4zon2r2rhY5yJffBZY8xSG3cAFE2f87cJ9280sXvzO8lC/FdYHrl742Ds7trzazYMdSd5ZYP2Fwx++WcGIWG17rfYLY+a7AW7mNRf+CEdO5mWe4NAheyyU5aAX7/q5vmKNb39RYBInEeOZjGRpY0wzAcbTTx16E0NHS85Nc/k87X0NFnfyMxY7mG3tSihqwRrHgaydNXeGzBuoBdGLjR+7Hgcfkn3hwdG8ztqky2dPh7w7XH4scMHyTj559e42frIAoG8wZo5Ect5GnRjsqUPm4fFPf3DKRz7oxKQ980g9XBfXYDpnsjPgmdxklQBt8ky+mUTaHSefuuxvFNgqgOsPu8FfF0qt1H4As0j4TQna/Fc7jK//nDc+ng7zQl0/CsvPIcHWynX8bVxi/Yd/XqddFFjgbytg8zIu/Lcd38SBdSfCZ67blMHbMgY6dyzG6bfug2HI4CaOnpZOKRtIvHI71ZOc2tVPHHqxjsXSG1+/s6/rUDyPH97cwbY945pRno5ZPp421V8jt9OxtzXQUxjzV3mcH/58bklhErP5qraMLz94eB59cXybYr/RmnxPU2DmKz9ycjgWZy8eOw/laVeWB1ziU5/YdYqcxDrrqJPEKTMWp10+9YmXW91RL+bIH7zXYEf217/y0XWgJo4CXAtfPOmPjULjQXMujikaGnb9cp7udmXEubFpR0EM/sefwskbqOsTgcJhF/uHFpi5dQLjKeeEKfM7sw2KNffpr5zx+8NutjfOyReG/QDh1JPdKxb5qJmotuRCN+1uqeiNr+/sj+Kvr+tMcMl5DYYvC++OIryXnzmyu/AR0f5n+uadi4puHp/FUxw3TodllGfhqlzcxbYdcPuKT84/v02x/Iqfj5bYMc1D2x/Tmxc9sTO+usmr3h4742zw0NQrr1MkhiQQkCQSYBNroWBLHwNNfzH62yf39ElZfO9M/dMB+27BhJlkdXmKNA967nfxtWh3hi6uerqWLYEtIkVGY8c6ypubro/q/lnfWoj4RbgO9MZUz9irPaifj2LXch722F588N+kdNGISx63vYsEkzwXBLuA3ZZ4/eh5ePEv9qzX72j9teF7eorEkZYB0zHH4JCzGfjM/4h/+iO7c7jA6IhFgfFZ5GzmMe9+6wfPOpWFs34dp6bin6U9UzsbBeFpUx6LVQr9lTNfdPOLieLwm/msggdUuRA7i7LV5UeMGVfeGV/97Ke/fkfrA9aYfh0n1ze55e0CQxAoiQEMqPOU1eO3SGvi+vt9r7Q5pp8FhI4Y+DsmRuaHvE6R1PW2O/cu0dzl33kWFzvGvGaSdy0suTcNRDWK/PvvJkuLjp3khefqi4Zxe2R33OZePN0iPrciuPHKH9rSyMv4vEge8gN6+41XCovTNbE8ztwe6dN1Fbn+PYinnnPJ9vhmnID20PgIjJXTx/UzjyMOdK4f44y//EvZR0UjQNptxCaSOPzTRz6x2iaug9VT4tFNWRyJw9WnyLjRir0XqFacnv9p8HANw30vC82csHtjdSux63nP+MpFCAjXi9bzKxM85pF/AOL8cepTa73T9Gc6xSeh+M4/3gw4/7Trhw68NvWpy7F2evS06dvKeDrzD0gPF18lfPEDdAa4i0iCSewk0cslBh8e7kbojSOfPhzIxCWH496B9t/Vulh0DnIVFLvLKpxa9gd1qvn8/nmk8eAi5vXvhW3Fkxf+YBKPvJoLQ7xq4Py+1zwWzIlT66P6fNS8yuGiYPvFsTHVM78nu/1uvx89mUceJ+DKjMGcHbtpQ555ypGc6I7azAdM6tZXplHyOEtsks/gSTqx2tQfxUGX+vQ505vDOtWNyXU8Xpix4cBFAeSNVWPRz58f7xjktidvTLCrGHZ+3x1y41UcbmDN8QZfscA+9f5LI5xa8Z/r0vH3nZTxbBnfHMSc8aU+ORnLZ5+6xGo31sW7SJQTIFAS7HObTsz0xy99Zwxs+ExM6icn8eXRxgLyrQn/aEJ9A12APU4XRhUF1zZ+B79x9YQfp9/Xv1934ouzi7PWzxLbimqfU2HBe+OVfs6DTxzYHbePijY81dpFRXGNay5+HCU/LPc4mB89uqknD2ODyflP/LSlrzb5s5d38qVef3S03sEk2VTbM7rZZiLpN4MaaPKol/vML+2MMxayucgnT19T7V+9EUdvHomnWLgu48I9/zpIvLsOchEQtIc8daGV+PALH1o/DZA58iYkPyqa8ZvIwi3u/GzUHMHol9zoEyMOjDudfuKO5LQdjeWlp4nZpMs1gT/ji1mnSBX2JqQseerVgUGPrF3bmV4f+e31U5ZPOfvEzjgWR2NcyHK2MJq3crZo5m2NjCtXxhafOGIhg/dfYWufg/ibnn1s++jpwee237DAf84r4zpODLozP3HmmTI6C1Leu3huw8kthn7tYAiZhKfBmQA4bGKxQ6xsguqU7adeecaXD3tiGBufsfmJhwe9fP6Vdmt2fSW7FRarW21bZgZ1fVanTf+l2rbFglM4vPN7+dPvXbsWsYzXhZi/s1q+nRdx95iN3cfbvbHtXxoxFn1yItOcHzbGxtys13Zt4pTt1ae/NnrHaZ/xPf7o5/qjw1dMf5tikgEiEA0CgyI75kYbzQVuIZ7kFC8fesf2YsO9E0Se8dGBZwI0459xob84bfa0eKpFZ93hKb7ZcoeCI+NkfMZcs+WfrCUXvuyaFjFj/k3v+1XIXLMxj6P5p865wZv6GSdlcR0/jjl6deCNn3j0yowzvjaPf+K00WPvOPW0v46vi6cNBwcdR5pJMTaANMrYZjviTT/tqZscyOIcH2HMI7nYzfxqND5z4SkCdfizmx19RGN8CiuvszKfjl9H1sLqHOtQe+P3KL/G1JP8N/h2QB5/fY56YuT8HYs1B2Xj2quf/ZFdbjnFrGswFfQ85ivWINhskiEf6dElRr/spx8yD3cusLdxGEMeZXzUTQ7veWUejXGn6byf6e/Vf75Oh/MNQPptp8XrnxVwR+ycmcsOZrfyT9bMEZM5OkdkHh5/islx4hk7R3vtyeXYXgw9Tb05zfjqN/T5MzgbnPKsa7C7iHSCJJOSNPWpY5zBtcmh7KtS/fSZ8Z1AHny56PXXT97EWByUQZ8k68DQtmLY/rXbxE9OZHjhodFfl9Tm2R8VxY+jbNqbz85/Wo7yFmN8ZXpzVKe/emXt+sg17eknJn08/mnTTr8+7EawTbB6emySKqf9LMHEMD7DoSfGWcv4d+WZHDOeNncz7Z4WlcWZ04yvnb4LNm6R+FERtsmHLpv86HJe0y9t6e84edDpr175NnzG0E/85JRP3A25Xjm9mhgktk/SHEuWOol9JSIzzmLUZpKTBxl8xhdjfviKc0yfLXOZ8ZXhSB4Kjest7JmnvHIim5O27LFxD8w/stXm8ZCHHqwyYx63zR+uxCkbQxnOyZ82x8bTH1k/deanj30eR7H2+nAc/x8AAP//4/JLOgAAIrJJREFUnZwJ1GVVdedPzSNUUQwWQ0xWEzEYjVFBMYDGqEA3Dq0SVBBMbBEihIA4QZYDmABL44AE0RZjWu2lcaVNFCFZ6IqmUUHRdEgMajRGoxQWWEzFUHPl/M59v/vtt+ve7/voE9/bZ+/9///3Pvedd+999yuyYNeuXbvLZCxYsMDpoN29e3cRw5yBPxRXIOPH4mqIF2cdfDHMI07MbLGMyX49Dk0TDeuIsZ5xfHPiI8Z5xDGPI/ZKPNaPuKE65CN/CBPzUc955BDDz5wYYx79iCXOGIzVZL/BAOlieS1cuJBwI5tDaOfOnS1mHox55kN54g6biRxyxjnguT55YmDIM8Rjoxb16c3+zIlv5PpmXD/mYy7qj2HAm8sbxrh1tLEGsYiLOeL6ERN1yMf1Rpxc8TFHzHyME4u+uBwzHvF+/gtqcGqDeWAEY2laGOLMEVi0aNFgAxRUJzYjl/zY3JwbCN+DxpxhfeLqa+1zaIOJ6VSmD6o8chFHH0PrFxd56mLz+qOmOLnknJOL2LnqD2kRQy/q6FvHmmKMwzU2pGNsCEfM0a+/TtoGywUFxsLELJ7jMScXC46Xm0Se9cTqz6YPdiyPrjk1xY/lIo55xNmnmCFtc1gPqLGIVyvGxEXrZiKWOfPxx/Qz15rEI2eovlhtxBNTmzk5fXELZttggiEzJDG3OS2xoaGGXHxe8awALzanTtZWg7y5ISsfGznG5ehrY5x59Id05GHFYhkZH+NiGzC85Q8YXPxiRs0xjSA3NR2qnzWG6seaca541oh1wPSXyCguOdpMBG9BrPkmOvHNEzMvNubIz1ZfLjj5cR5jOY4PP9bLehkzlAfjMI9mPA7m7WeoprGoAQ8/5pybi9rMrStPvHEwxsQQY1i78zrcbMc/crKm2mpGH2y/wQQoYPFIYD72jXJhmZ+bG8ON1Xfh5O1lqMZsurFvca4va9mHeawYc/rkjGGJxxx5hhhy4mKsQ3XvOZ7Xn4+/eNjqO8eS5yUvrx9MHLl/uOpGnchhnnXl9BsMUG6GmIMcwwYydigfsepgwbpgfbHxgJIzzjyOXD/mmLtg+GDVmQ9vrDdrqIfNw3rErZkxkZcx/7/rV1O9uP5YHxwvcVoxMWcsWzHYPNTr69fJnqjKEqhAT6gfGMMi5mOxyI1x5nx4kYsuww814omrFTnEs0+MYXxIJ+aiLrwxX82MyXjyDGq4li4y8259I7HHMb0Ydw5/Li21hzhyxaAXcfh5yCGe5xmLltpTN/kC+2QAxhwCczUkHqsec4vPxfc5Sv6w8kZH85GOoQMU+yFvHeu7BrlaeeajT0x+7FFujuHDJ58fs4i1Lyza1jOvRQMMj5JyPeLEGPaXdTz+xrXWV9O4etFvmEpolUhkEA0YYx6HQjHGvIlWLUfmj/EyFx4vD0DWG9PJ9eWNWfvL68/1xanjgbY/62rJM8zLG7PWow97IZb5xBi5vrqxfuTH40VcHePW1CdPDevHOLXkm8ePGq6/vweLAJvFSnIec8wtZAMxP5az1hBfjjpD9TNGLDb2Ic6YPpaY8cgfm8sxr5b+fLTkYOMHMx8NN5R11Brz0QRj3hpj8YzN+rm+euL0c73+EgmApIRcMBMVFB/5UUec+egzd6fnAz5WP9aLWhE/VH+ufF5fxMc6zof6sG7WguMHxDzmXT+xGM/1rScm+1k/57MefTDEMVebuXrGxOmDYRiP+hEzdQYD5Afd0WcEIClGTpEYM24xc5krDhuxzB9pfTQYsVYX6d6jfozbf4yJzTH8ITzxsbrk4hBnTD1rYp2LiVZ8jMW5+uDU0UbcbPOIj3pyjOHbT4wZjzpTG0ySgti5BHJezpAWuXgAxGIZQ5ysn/kdc6bPnJ8vX51s5Q/1BnYoTyzjIy7mjaNl78wZ0c96HWLmPepE/bl4MwrTs6hBRn17Utd4ZBPzRNE2GKdDA7MRyCmsoHjj8/XVyg0P6aqJtc+M07cPfbnWMY4Vay9acvKcy5dDnKEf8V2mezdvTJy1yHv8jYnFEjOOnWv9cq0Lh5H9GHsk9dVpouEtx/u6VXy3SYOB10/F9IHJhObMadGJ88whFzHmY8xeso6+HOvLzXl1tOQzBi3yvPwAxVsnWvjko7UPY+KtFfXEZBvry488YuqZt679RE0wmU8sasjD8nL94BhjfHnWB5t1W6wC2xZXKIIAMBTrvJmiLsZ4tOrlhiOGuTjjQ/XN8UDl4a27y6rlMx+wufnoxHVwYBhz9QdG3oNbdpcHt3TPkJYs2lXWrl68B98+PPBRXx0085Dn+mfrL2PRMqau/lh99a2nhedcLW3M+ZxMfeuJVaNdIk0SjCKAbSQLKQBGvpacPB70MV79J3eV7TvatLz0WSvLf3vaqubAseZQfTVv+NaWcun/vqc88PDu8rRfWVauPHe/sqj7t5BTOjjoZC11jIMjxrqsbwyMw3WAO/nijeUHG7a31NG/urRccc5+UxsMHbWcu358hvVjjaGcdcVpI7YJTt6sF2sQG6sPjrz9+PlSl1iuNynTx+1PXq6vbn+TrwBARi4QfedysLFZxYkz51nuEb93O24bZz1/7/Ka5+2t26x1p4LV6bRKeeb5t7fN1T76qnnpq9eV449Y0cOt3wfCJObiPEBGp7Gvky/ZWP7t9rrBav1jn7C8vO/sfaeOk+vGOuDnmuLARKwcbKyLL84NQCwO87mW9eNGEAtffK4XtZnLsb6+vOg7b7xK6O/BomguHEkZF33mcuWwZ4846/Ya7y5LZ71gTb/BxOZGWcjMQSnl6b9/e9m2feb0fcFvrymnPHv14Aeh5lBf5NSNvQ5h6d++yLcNtqE7DbvBMg8/1pCPFmtiUN84vseJucP1ixOjL07fNeHzij5Y1xJzxKmjtjjjsU8x1tOPa4KX60+dwUhKBMzIBAuQExt5MS+GM9iRr92A2/TOPHF1OfMFa5tvDK3MFUD8musfKB/6/OaGWb1iQbn+sgPL6hXdNTLWjxzm9hjj4P0AiMvHOiLPeNxgz/i1Fe0MJn+IZyxi8jr1tXJyfX170Y/azI1HHHPj4rM/FFdPLX1sHFmfnJz2JD8Wi0SBOe+ujXHnCkcdNthT2WCTTRQvkX6DhvjEYvM/u3tn+cldO8qTfnlJWVivQnKoFef2bdyesh75sfpDemywH27YWXvaVY594opyxdn7zRzI1CvaDuq7DnX151tfLWzkjK3NOHhrWZuYQy39aCNePWJj88jtMXXCmPUDgjiEiYJijE03Vy+R4R7szOft1V8i5UW8MbWwP7pjR7n1h9taH0uXLCgnHrWqX+h3frytfO8n25u/csWidm9224+2lxu/vaXc+oNtTebQgxaXo+t909MftzzKtjlrc8C75V+3ln/6t23l/od2lafWHxTPeuLycujBi8tL33Fn+eEdOxv0GO7BXruuP25otFdZWG7854fLv/xoW/nuj7eXLdt2N+7hj15ajn788rLfmu5Hj8cTu/GeHeXm27o+ET/+yJX1Xm9b+cQXHygbNu0qh+y/qJz6nJXlcb+4rPBj5+H6S7bUlp902LJ2Fv/8zQ+Wb353a9lZL3dPPHRZeXKNH/nYmXXe/J2t5evf2VK+XY/fsnrsDv+lpeVFx6wqB+073YvHYMjmjTj0ebEWrwzMGVOXyCjcA+qOfSRjiEetfoPV+ZnP7zYY2NyotdTRv/pzm8tH/qa7RPLr8RsfOLhxwb3rL+4rn/rSA6X+9inLl5Xy8t9aXT76tw9IbR9G/fibf2y9tL3rzHVl6eLps+POepZ996fvK3/x5Qcbvkq1YR8XnrK2fPrLD8xssMcva78iLQLuxxt3lvOu2tTOsvLI0xf1+WJc8sp15bgjV3SbcbL+r9Yvwh9cdbdS5U0vW1su/+Q9zff4nP3CvcornrO63ove0ePY5N+oG4t7U+pZB8BLnrG6vPGla8r5tZ+bbtva6hMHw/84hhe+fE150bGrCc854noA25fEVj/sFfFTjyn6YAVmAYWw4DJWHwtXTKezoBzJGWwSz/dgURM8L74xaPCNwP/AZ+9vG4x9smjh7nLzVQe1lsizwT75d5ub3/bFhF8Fum9U9Zlb/7Uv2Ku86r/u1XS7/ko5+3131Q9iS4uBmxrsTUNtn+6uZ6Nl5b31DGZ///D9re1RjHWaLjUZVW83N/gT3dOfs6qc99v7dLn6zgb7/St/3npsvIpj/Yx2RqgyZ//3yQY7hw1WA7HH6voFsj7HbsXSUrZur42DDetnvqAeN2Q++ZYDymGHLGm14DA8JsyJ4cecvZFv/TGZDPG4bV4fmA3+ihwqogjWIhEX87Gh2vLUGew1J67qb/JtaBo/rU/ug9c+0G2wWmRRPV43X3VgK7fHBpsc+KcfvrScUr/x1P7c1x4qX/jWw1179RguXLCr8g+pB647eF//7rby2rrBGKyHb/cFJ68tj//FJWXrjt3lw9fdX88UM5cwPpijH7+0f0yxY+fucuJFG8vP7+sun+gcf8TK8vzfWFmWL11Q/l+9TF/11/cRrt3wYe0qn3rr+vbBsrav/cvWboORn/TP8eWMd8A+i8vGTTvK+fVX80uOXdWdwdgHkw2/ZtXC8saXrSn77b2wfPEfHq5n2XoGrgM+WhyfY+ql+bTjVrczHWfoG299qObqIqvGr9f72Y+8/oDGGXqjP172BYZ5ftA6xG3YSu627QgipxHPBaEO4YxTof2KZFL53IOdcWJ3BpE3l65nMD4gzmBfr5dIh5dI/aN/dXl7EGt97IUfvrvc8M2HWn38a/94fTl4v8Wt7xe/bWP5j3p544CzeT9zyaPqfc9iYC1P23/wp3eXr9UzHJuLDfKMJ67sf0V+9G83lyv/arKB6vpOf+7qcu6L9576UG79wdbyu++8sztzVN3DDl7Szh6sv10iqz5nIdbH/06rZ7lzX7y2bpDWRnvbXjf7UWdvaPU5jnwRrr9sfbuvcwO85c/uKdd/46HWN1qH1y/JJy7qNpCf2yl/dGf53k/r87y6lqX1C3DTld3VYKbSnrP8OYmwrn62g/dgNgKYbwLfglggi+Cbl+uG6XJusI7JPRgbLA+bVYO89TkDcA8GhgP79XqJtMaffPr+/hJZGyl/efH68l8OXNL3hA6XsNe8exPT9kF+4Lz9y1GHL6t/etpVjj53Q7cZ6gF/4TEry1tesbbzK9b6P62/Xl/4lo29ZnxMcfrld5Vv//vWps1N9P9930Flcb1/Zh1eQphf9JF7yg231DNp3UCMr77/oHrT3V0iz/3TTTXcba4jH7u0fPD8/ftaHhc22NPOnnlg/cKjV5W3njbTK5pf/seHywUfrJu11qtv5ZJX7Vued9RKUv245vrN5erP3d/7N15xUFm5bNJUjcbj77zp1Zy9GO9FJpMYb/P6xpgSBRuF9MHFOH48gOSIiW+T+kboKWf9VLedwc58/preZ+IpfSoYHG7yr7m+OyiL6zOKb1x9cF/LMxgf0MJ6dvMHgHR64hHHiRf9rO/fvwR8vz6Zf1n9deg90mX1LwTHhb8QqMHanvP6DeXuzfXSU//v2F9b3u7BiD/z/A3trwycEZ77lOXlsjPWSWv1PG6cqdhIjk9cuH89wyxtZ7BzuQerWoz3/N6+5Zn1lysjHs9t23eVo86pX4b6f5ztzj9p73Lac7u/iFjjth9tLa+4rLvcw/+fr9u/POWwpf26iXHLcPHHuh8R9Px371lf9qrPFlkLL4Z1m1PfYnxoLm4PXv1gux0holpAceOEVJsOFdhDeNIohPlssMhnbg1re4lEbHE9hbHBOu2ZX5H4K+o38Sv1GylfXe6Pjn/Tnhvs72/dUl539ab+gF7z+v3Lrx+6pOe3IpO3V07OVNy/HFM32BX1T0U8huAM6Di93uuc+6LpP4ORo59/r49aTqp/z3Rc/Dv7tLNL3nh/dfEB5dGPmr7xhtNdIm+vWt118031V+DJv9n9CnSd3/2P7eXUS++0RGE9T/rl6Q32+ZsfKm/7826DwfvSew4se6/sNOlTrV6kTjyentFjLs4zt/8n0wprJSmsj0VkKJ5z4iq8/amoMvnC9Gcw8/AY+tou2r33G6y6i+rNeb4Ha48Xam75kt3lK+/vHmHEs+Km+3eV497QPexF8bIz9i3H1bMNN+BnvPvn3UGtjV513gH10jn9gYBnvKw+aP0+fyqqOB538LfI+hup3l/OXLZe/qzV5Q31MQMjfxicXU67vJ6p6mCN73zNPuXZT17ZncE4s9UYm+faPz6gHLhvdw/YwPWN4902WD2DgeM4vvnla/fYYDwT5AzGWa4CyzVvOKBtMHQ8Hmywt/+ve5tOFW4bjDMYPXk7NPb5ojP0+RCLOfgNV4t2mZrNolko+01xHm/U9jkYGtzk50vkXNptg3GJrI17D0Zpen7np+7tn1/xHIwzWB6ewYyzwfhj+V337iwnvPln/SXyD0/dpz5D6v6lh1gs/R1z7u1lCz/764h/izzhzXeUO++p/++s6v8d8dhl5UOvm37CDx7+Z+ul6R0frx/sxP/r+mPiFw5YPHWJROPzlz6qbTA4DtbJJdLnYOTyBgPDBuMM5mfpGSxq9RtsIs4ZbHW9IsORZ91o0RjLm7OOuP4mPyfY7bz85x4QMiYWZ24R5v6M7XgzG4xc/BWJz7ChWCPqscG4BwPnYwryfOO4B+sekNZnP8sXtg1mfS/1d927Y+oS+UevWltOqE/M0XtqPQPt7B47lcc9ekn5WL03QpuX6+dh5dlX1DNDxXP64B6MMxjjnCt+Xm6qT8sroX0Brrt0fdl/cotpfXgvefvGepmsv97qwL+lXub5k1e8RFITPmcw5q1eY5SZDVbL7Kq/ZNlgL33WzI8l8N/7yY7uEjnpZWyDvfWjd7cvxILaQLxEomF/uf6kjSkjnmDEe7bcY4MBZFECIHnaNKdoXDy5OPiAGR2322B8Ozm5/4/6kPN3T+j+1MMHGHWsa2xpvVIw739FVg3+idlNV65vcXL9Bqv1vAcb2mAnvKne/3QnoMIG4xJJ/Us+fk/57FfrI4w6uNk/76S15dRnr2y9E9t4z65y+uV3tudcrIH/HfOE+iS//i2Sccv3tpaz3ttd+vB/oT7iuOaCdWWfvbr/B3h8Zpd/6r7yf/7+wbZ+MDxpv+iUNW0NX/lnnuRPfuHW+tfVP+R7ifRDw3KJfDoPWmv9XfX4Xnjquv4SiSajvwejaD02cYNxrNDhDMYG4wuxsK4/brB8/K3fqc+8G8cOjV6nThrC4tg4xgQihrkF5cvDp0T3HKwCla9V2Wzi6xSVGcAE960Pdjfz/QarevkS2X5F8iS/5txgqDHog9fdm3eX499YL4VdoXLZq+s92BHLW/3u/mzyhHzS4GPq3x6fcOjScm/91cjfNPlzDMN+j6lP8uO/B7vg6rvKl/5xa59fUr8Y/MPINasW1Q24pdx5b/f3Q+rT4xfe1T0aoDfOYDxnc/3X1Wdbj9qnu+luRSd1+3uwSZAz2EnP6B5BoENvbjD7/PAF+/U/Woy1m/yP1pv8yTF2g7EpPOOi90iH+pE3eJMvIBdBIMb0o83cLjd9iawiFdadzci30dZT3/QJVty3PnRIS7PB/uxv6t8XK9wNJtfHFOydFfWfU8d7MPtlE/ErsvlV97Iz9us3GAW+8M2Hyx9+ZFOpf9do9cTFfjx7AWCD8S9aHfc+sLM+jb+73FbvgRjWtUdxy+ufb95/zr710cHMY4i4wTgMXCLXr5v+T/7R2WOD1R8TJz2zu8xTD0y+B2OD8SvSAebamx4sb//zei84xwZTM3JdFzG0MkasduoSCWFoZBF8Y1q5+lr0+jMYTj2CnkVwG4+jSmKyYvNspFuu7jbYh6/bXP9cVG/yKyw+6+Jb9/7P3F8+/sXuTyT+irS+B8ENZn2fg1FZzA/rYwQuVXds4p/ktKZqR/WY1P+98vi9yuYHd5XP3Nhd5rgH4xLZXwrawS7lYzfcX66+dnPZzhmP4zlZFnr8q4y3/c66/pkTtYnfUv8MddZ7uTHvzlo3vOvAsm/9008c4OIlkkv5m0/Zp53B6L8dx0r4wU+3lpPfUZ+D0X+NX1P/DPTkx8xsMDTbBuNXZB3o8qub4+bZyzjWY6M+MQY8Yq6/i87EzfcbTEC2Q6fNWBQhR26COHleNp8bypyol3XNRY4xsPaV57PVzzXw+Y87vvPjreWO+nD20AMXl8ccsrQsmfzrC7XljdXnksg/IeI/EjmsXm5/af2Ser8384G4hsjP2vjkXVdcR4zbizh1IsZ6Hn99MHHk+FDNiJdvLXP20jaYScEmAVtQIlacPGIRZ96YPjhG5umLkxexMedcXhOdvMElbk4tOeblmLcWdjaMvIiXYy7WMqa1N79wxO2VefxC4882Ii/WjHP4Eaevrmv1OIhVA5yYPBdrHMuQC29B/bXVtrAFtBEkaSiXcfkAmVeDA0sMLax5tbXgHeLIySMX5xFrDTFYNfIHK09r/dhXnIvT5lxevzgsWHu2DniGfWW9lqxvcvWzlYdVC4zxXNec+cgxh5UHjrl+zDnH5vVP3eQDYFg0i5FDwEL4ecglzpzGYwxubiJryAXrUAOrRuxDrDj9zJfrAR3Cx5hzdbBR27wxfXDMXb954kPrlyfOxyz6WnDM/RzQGxriyanNXK4xj0PEyyEmjphDDfzME4NtvdZG+zOYYpGUY9mPgsxdOJbhAppT32w61jDXGgqbyjjWusb01dFaX38IH+tEnTjPPP3ZdMFYP68/8mJ9dYnx8njpy9OKRz/iiYOxvngwcRBXW46WuDw5Q3xyc9Xv+6iTvoMsHotYPGNswHj04xwtNXLcOtFmTN/wHBvQPmK9qJvjuY6+HPWIO1cDa0yevpghfwgLnjGUyzF98FEff7YhD+tGFk9sNq18/NWSny1aTTPeg8UCFsxCEYNozJOzkVxQPfmRB9a4PHWMi9cXpxWPDybjrS9eC4/hAZcnfqyeOPNz1TcvXr59DMWJiRvKw41x8cbUxlqfecyrPxQfwhmLPLj45LTEGP1/tkYiJhXqYN17zBPBz2OoSMSqKzf6Ud941JdDzHyMGVfHHFjn6kW+eXnqYOWJkaeOdgynpnnxQzoZIxY7hI/5obl69h4xWQ8srxyXMxa3xhCu6dWd3e+S2EgWVMi4vsLRkvOMYFx85kefecYN8cFEnhhtzBvTWmOIbw7sUD7G1dPaNz5z1x/j5KyhPrE4Il4s+TF85DqPGsa0amY9OOYyFj/jxeRae2hUAKMXYB6HBK05Cw7hxWSb60QN53LUta7WfMYb10a+sWhjL1w+8oZQHxwv8mqiY17NmDOWcWDkecnCt37Mq0feOTb3GWvNNrdu1CKmfrRZJ3PyujI++lNP8hWaAtQm4gBjs8Q9UMZjLvLAMTxAMedcDXx7yXoRI27o4GR+9merSc66mTdW3+NgL+qrk9evTrTMPT7M59OHddXJ9e0jWjH2Rg5+rB/x5o1l3pBej62ibSUT0+IQfA7jgiVowStMLBYVg1XXvL5WfX04YpkzyM0Vi3w4+QPNfDCMzBvC5fpyItZYpzqzBuNi9XN/maePhSPfOJ8Pw+NnXNyQ/pCO/chTJ/LHeHDky9Ma7/9URAICwtFaWKtAtDbQi07OevpqY4lFPAdI3FDduNBYM8/VJa4OMf/BYOzf+mDlkecVc+RzffKMqIevjjm18BnixZmPNs471p71Yxwt1mdPWDR40bd5OGJyDeK83KjMI585w3xz0pvaWms0rfo2ddOV3KkDg24jTYri24DxaM1HTfHkhoZYcfpi1c/56DOPPDlqiMU3F/HisGJj3hj5HFePnCPG4tx8tOrFGuSNMycXfWKMzOmi01xx8ufi5LwnILW14tB13mpVwuB/2Y1Q3LU2pOBs1iKxEHibi3GxWc/4WF010GTYK3i5YOQzz1h49sScEXXVJB518B1qyiMONnLFEs85/MiN2BgHxzCmrzWOHdO0V3qTFzVbgckb2LE1RJw6YvF56feXSBuTbMP4EKIvRhvzzBkZH+MRD9ZcjqtjXl9OrCHGWPSzbvbVI+6BIeYYw1sLHJg4xnLEI1YcMV65vnFx2lgrztWOOGL6s+llnLrEh/pSExyYOMy1f02BY0CgfiQ5j5g4J599Y8RjHbXyGcQ4NvYQdZkP6RmDK975UO2YEx9rkh8aYK0V8WrAMS7WWIwzV0eueesO5cewcHIOX41cL/rW08rRGsdmTXPEHa6jP4PlhGCBeSMYlxeLGos242MO7mx5sNaPuMgbqy+ePCP6cd6SKW8MG2vhw1UTn5ExxKzBfGxEXpyDjzVizSHdjI38Ibz5WHM2XK4feWph4+ifg9mcBcZ8yeL0LSbPODZjzcnRn82qq1bm6otTK+KJRR+sp355Ma/GmAUrT+3oGxvjg7WemBxTT1z25WlzPuuJw4qNMevEWJxHzhA25uFNncFMQuSMweADwI855tGPheKZJmKYi4vzVmTyFvHWh5Prk3NjRD7zXN9aWjDMebk2Ygzj1uyi3Ts5BrnZ6oMDw4h6xl2LmAZMWGuQG1rnbPVdv7Vdo3E0yTGoYz/4OU7MEXNo4cPN/VlH3f4eTCEtQAYCiuFDZJjX18ZGGnDgLerFeYS6iFwfjLUi3nmur36MexDMRb2Iy5r6EW9szFoj6hqD4zzmiXt88/rFgxka6mjdAPr0rrZaQ+uJMbnWixrqmxMrv79EArBgBpnDKp6FyTGGuF1mJodvA+aw1s9zfaz1h/jk4wAT+3EORr4xfXIxFufkGHxAY+vvEDNriXznYob6yzGxcsn7BTGHjf3HeOQxF+c85uWZ08dGXMwbj1jm1uk3mCQsLw6gZMDOFVJAX+vi5Qzh0MrxGHNuTbWsoTWuJS43z2O9jAFrnhwv1+8cDAOfIb454Y28/TjPeDUCrU0jz/pyhzjiYy+xZuREDJoxZw3j5PwCqSd+SMc+5OMb22ODAZrvsCh4CxvTn48WHF6RE+dqRIycsQMhRx3wQyPmmYuLcXiz5cxj5TNnRJ5+S6Q3v5gp3LtRxzm1xtYPxgEu+sTt03jGmFcj48jzsr64PXg1sMeRJ6SgRG3OSReffXljVnzME5ut8VgrzqOG8/nmxVM71s/9xRwc83PVUV+cvlYdfOaz4Tw2ERf5aMg3rk+OMRaPuQacvMmPtwexvlh19afOYCRpfogIwXgUsbAxMcRjM/Dzt1QsOfD6ahnHxjGUH4rNVj/WQxs+L9dvPdcnRp75bNEAwyuuN/vquVn0xcX4WA05WHj6Q/Oc02+k+gbH3rURM5aXr43ctv4aaGcwD4bNSdAC4+XCJ7Q9FmZcnno5bj5ascbgxFj01TMfc/CH8upi5RkbWn/UZM5raP2xlnN157L2AW9oHvkRM594xIzNY7/Wj9iYZ+76I2ZoLm/wv4uUIAif4vrMh/49Umwg520+alhnzIKVJ8ZY1icf64uTpyXOUFffvJYNx/Cf+xifTTfnrCF3yOb6kZP1om9/8QOfSyvWVyvWI68Glpf64uRFLXkxx+cDt/+PPiREEDEWApA4g0LMe4Hqx2Ej5Jnra7OOcTVy/eyDm0/9IZ5cLHXB5Lk8rfkGnLxFnnFj+ti8tphjLif2Qjzy7CNiwYwdf3Fz6bhB8xeIOPW16LjJmDOskfvsst27/KkzGMExMcmKWsQ41lyMgeNFLuejBjlxmR/92eZRX2114cX8kM5QfXFRj9iQH/XNiyU3Vy+PpH7uS649zKe+nKg1xDePHctnLXGzbrDYZBRnrqCW2NAY0pATczRkXJ2YJybGvL6LMY6Vay76uY68HI9+nKuvtvxowY8NcvmLDHa2GjmntnFtjDvPFqxXpbgG4nFkzYzFFxMtGmL/E7AKXaYE1pgUAAAAAElFTkSuQmCC", + DB.PolarDB: "data:image/svg+xml;base64,PHN2ZyB0PSIxNzczNTU4MDAyMzQ5IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDMwNTEgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE3MzgiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNMCAwaDQ3Ny40NzA3MmMyNjMuMjgwNjQgMCA0NzYuNzIzMiAyMTMuNDMyMzIgNDc2LjcyMzIgNDc2LjcyMzJ2NjguMDg1NzZjMCAyNjMuMjgwNjQtMjEzLjQ0MjU2IDQ3Ni43MjMyLTQ3Ni43MjMyIDQ3Ni43MjMySDBWMHoiIGZpbGw9IiNGRTY5MDIiIHAtaWQ9IjE3MzkiPjwvcGF0aD48cGF0aCBkPSJNMzY0LjkwMjQgMTAyNGMyNC43ODA4LTEzMC4yMzIzMiA2Ni44MzY0OC0yMzIuNzk2MTYgMTI2LjE3NzI4LTMwNy42NzEwNEM1OTkuODQ4OTYgNTc5LjA1MTUyIDcwNi41NiA1MTAuMDQ0MTYgNzE1LjAwOCA0OTkuMTU5MDRjMTguNjQ3MDQtMjQuMDQzNTIgOS4wNDE5Mi01MC4wODM4NCAxNC4yMDI4OC02MC45NDg0OHMzNS4zMjgtMzEuMTkxMDQgNDEuMDAwOTYtNDkuMDQ5NmM1LjY3Mjk2LTE3LjgzODA4IDUuNjcyOTYtOTEuMjA3NjgtMjIuODk2NjQtMTA1LjQwMDMyLTE5LjA0NjQtOS40NzItNjcuNzY4MzItMi4zNTUyLTE0Ni4xODYyNCAyMS4zNTA0bC0xMjkuMDU0NzItMzEuNDA2MDhjLTk1Ljg3NzEyLTEwLjQ4NTc2LTE1Ni44MTUzNi0xMC40ODU3Ni0xODIuNzg0IDAtMjUuOTg5MTIgMTAuNDc1NTItMTIyLjQxOTIgNzMuOTYzNTItMjg5LjI5MDI0IDE5MC40NjRWMTAyNGgzNjQuOTAyNHoiIGZpbGw9IiNGRUZGRkEiIHAtaWQ9IjE3NDAiPjwvcGF0aD48cGF0aCBkPSJNMzM5LjY3MTA0IDM2Mi4xODg4YzM1LjE3NDQtMjkuNzQ3MiA1Ni4wMTI4LTQ0LjYxNTY4IDYyLjUyNTQ0LTQ0LjYxNTY4IDExLjQ3OTA0IDAgMTkuMjIwNDggNS43MjQxNiAyNS40NjY4OCAxMy41ODg0OGE0MjkuMDU2IDQyOS4wNTYgMCAwIDEgMTQuMjMzNiAxOS41ODkxMiA1LjE4MTQ0IDUuMTgxNDQgMCAwIDEtNC4wNDQ4IDguMTUxMDRjLTI2LjM1Nzc2IDAuOTYyNTYtNDUuOTY3MzYgMi4zMjQ0OC01OC44MTg1NiA0LjA3NTUyLTkuMjg3NjggMS4yNjk3Ni0yMC44MDc2OCAzLjk3MzEyLTM0LjU3MDI0IDguMTEwMDh2MC4wMTAyNGE1LjE4MTQ0IDUuMTgxNDQgMCAwIDEtNC44MTI4LTguOTI5Mjh6TTYyMS41MTY4IDMzMS4yMjMwNGMzLjUwMjA4LTE4LjczOTIgOTUuOTQ4OC01NC4zNDM2OCAxMTUuNTg5MTItNDAuNDI3NTIgMTkuNjQwMzIgMTMuOTI2NCAxMC40MTQwOCA0MC40Mjc1Mi0xMi45NjM4NCA2OS40Mzc0NC0zMC41NjY0IDMzLjI4LTEwNi4xMTcxMi0xMC4yODA5Ni0xMDIuNjI1MjgtMjkuMDA5OTJ6IiBmaWxsPSIjRkU2OTAyIiBwLWlkPSIxNzQxIj48L3BhdGg+PHBhdGggZD0iTTExOS42MzM5MiA0NjMuOTg0NjRsLTQ4LjQ4NjQtNDYuNTkyYy0yNi43ODc4NC0yNS43MjI4OC0yNS45MTc0NC02OS44NDcwNCAxLjkzNTM2LTk4LjUzOTUyIDI3Ljg1MjgtMjguNzAyNzIgNzIuMTQwOC0zMS4wOTg4OCA5OC45MTg0LTUuMzc2bDQ4LjUwNjg4IDQ2LjU5MiIgZmlsbD0iI0ZFRkZGQSIgcC1pZD0iMTc0MiI+PC9wYXRoPjxwYXRoIGQ9Ik02OS4zOTY0OCAzMTUuMjM4NGMyOS43ODgxNi0zMC43MDk3NiA3Ny4zMjIyNC0zMy4zMTA3MiAxMDYuMjA5MjgtNS41Mjk2bDQ4LjQ1NTY4IDQ2LjU5Mi03LjE4ODQ4IDcuNDc1Mi00OC40NTU2OC00Ni41OTJjLTI0LjY0NzY4LTIzLjY5NTM2LTY1LjY1ODg4LTIxLjQ1MjgtOTEuNTc2MzIgNS4yNzM2LTI1LjkwNzIgMjYuNzE2MTYtMjYuNzI2NCA2Ny41NjM1Mi0yLjA5OTIgOTEuMjM4NGw0OC40NTU2OCA0Ni41OTItNy4xODg0OCA3LjQ3NTItNDguNDU1NjgtNDYuNTkyYy0yOC44OTcyOC0yNy43ODExMi0yNy45NTUyLTc1LjIxMjggMS44NDMyLTEwNS45MzI4eiIgZmlsbD0iI0ZCNkQwMSIgcC1pZD0iMTc0MyI+PC9wYXRoPjxwYXRoIGQ9Ik00NDQuNzMzNDQgMjk0LjA3MjMyYTYyLjIyODQ4IDU2Ljc1MDA4IDAgMSAwIDEyNC40NTY5NiAwIDYyLjIyODQ4IDU2Ljc1MDA4IDAgMSAwLTEyNC40NTY5NiAwWiIgZmlsbD0iI0ZFRkZGQSIgcC1pZD0iMTc0NCI+PC9wYXRoPjxwYXRoIGQ9Ik0xOTEuMzY1MTIgNjEwLjg1Njk2YzAgOS44OTE4NCA2MC44MjU2IDU1LjI5NiAxMDAuNzIwNjQgNjIuOTU1NTIgMzkuODg0OCA3LjY2OTc2IDg2LjI3Mi0yNi4yNzU4NCA4Ni4yNzItMzIuODI5NDQgMC02LjU1MzYtNDcuODIwOCAxMC4zNDI0LTg2LjI3MiA0LjE0NzItMzguNDUxMi02LjE5NTItMTAwLjcyMDY0LTQ0LjE3NTM2LTEwMC43MjA2NC0zNC4yNzMyOHpNNzI3LjM3NzkyIDQ1NC4yODczNmMtMC4wNjE0NCAxMi42MjU5Mi0wLjE5NDU2IDI5LjE3Mzc2LTEyLjM2OTkyIDQ0Ljg3MTY4LTguNDM3NzYgMTAuODg1MTItMTE1LjE1OTA0IDc5Ljg5MjQ4LTIyMy45MjgzMiAyMTcuMTY5OTItNTkuMzQwOCA3NC44NzQ4OC0xMDEuMzk2NDggMTc3LjQzODcyLTEyNi4xNzcyOCAzMDcuNjcxMDRINzIuMzA0NjRjMTExLjYxNi0xNzQuMjc0NTYgMjIxLjMzNzYtMzA3LjA5NzYgMzI5LjE0NDMyLTM5OC40OTk4NEM1MjUuMjA5NiA1MjAuNjAxNiA2MzUuMjU4ODggNDYzLjM3MDI0IDczMS42Mjc1MiA0NTMuODQ3MDR6IiBmaWxsPSIjRkVEQkJCIiBwLWlkPSIxNzQ1Ij48L3BhdGg+PHBhdGggZD0iTTExNTMuODYzNjggMzMwLjM0MjR2MzYwLjk5MDcyaDU1LjM5ODRWNTUwLjc3ODg4aDk0LjAxMzQ0Yzg2LjkwNjg4IDAgMTMwLjYxMTItMzYuOTA0OTYgMTMwLjYxMTItMTEwLjcyNTEyIDAtNzMuMzE4NC00My4xOTIzMi0xMDkuNzIxNi0xMjkuNTg3Mi0xMDkuNzIxNmgtMTUwLjQyNTZ6IG01NS4zOTg0IDQ3LjAxMTg0aDkwLjQ2MDE2YzI2LjkzMTIgMCA0Ni43NTU4NCA1LjA1ODU2IDU5LjQ2MzY4IDE1LjE3NTY4IDEyLjY5NzYgOS4wOTMxMiAxOS4zMDI0IDI1LjI3MjMyIDE5LjMwMjQgNDcuNTEzNiAwIDIyLjI1MTUyLTYuNjA0OCAzOC40MzA3Mi0xOC44MDA2NCA0OC41Mzc2LTEyLjY5NzYgMTAuMTE3MTItMzIuNTIyMjQgMTUuMTc1NjgtNTkuOTY1NDQgMTUuMTc1NjhoLTkwLjQ2MDE2VjM3Ny4zNTQyNHpNMTYwMS4wODU0NCA0MjIuODYwOGMtMzkuNjI4OCAwLTcxLjY0OTI4IDEzLjE0ODE2LTk1LjUzOTIgMzkuNDM0MjQtMjMuODg5OTIgMjUuNzg0MzItMzUuNTczNzYgNTguNjU0NzItMzUuNTczNzYgOTguNjAwOTYgMCAzOS40MjQgMTEuNjgzODQgNzIuMjk0NCAzNS4wNjE3NiA5Ny41NzY5NiAyNC40MDE5MiAyNi4yOTYzMiA1Ni40MjI0IDM5LjkzNiA5Ni4wNTEyIDM5LjkzNiAzOS42MzkwNCAwIDcxLjY1OTUyLTEzLjYzOTY4IDk2LjA1MTItMzkuOTM2IDIzLjM3NzkyLTI1LjI4MjU2IDM1LjA3Mi01OC4xNDI3MiAzNS4wNzItOTcuNTg3MiAwLTM5LjkzNi0xMi4xOTU4NC03Mi44MDY0LTM1LjU3Mzc2LTk4LjU5MDcyLTIzLjg4OTkyLTI2LjI4NjA4LTU1LjkxMDQtMzkuNDM0MjQtOTUuNTM5Mi0zOS40MzQyNHogbTAgNDMuOTkxMDRjMjQuOTAzNjggMCA0NC4yMTYzMiA5LjYwNTEyIDU4LjQ0OTkyIDI5LjMxNzEyIDEyLjE4NTYgMTYuNjkxMiAxOC4yODg2NCAzOC40MzA3MiAxOC4yODg2NCA2NC43MTY4IDAgMjUuNzk0NTYtNi4wOTI4IDQ3LjAyMjA4LTE4LjI4ODY0IDY0LjIxNTA0LTE0LjIzMzYgMTkuMjIwNDgtMzMuNTQ2MjQgMjkuMzI3MzYtNTguNDQ5OTIgMjkuMzI3MzYtMjQuOTAzNjggMC00NC4yMDYwOC0xMC4xMDY4OC01Ny45Mjc2OC0yOS4zMjczNi0xMi4yMDYwOC0xNi42OTEyLTE3Ljc4Njg4LTM3LjkxODcyLTE3Ljc4Njg4LTY0LjIwNDggMC0yNi4yOTYzMiA1LjU4MDgtNDguMDM1ODQgMTcuNzg2ODgtNjQuNzE2OCAxMy43MjE2LTE5LjcyMjI0IDMzLjAyNC0yOS4zMjczNiA1Ny45Mjc2OC0yOS4zMjczNnpNMTc4OS42MzQ1NiAzMjMuMjU2MzJ2MzY4LjA3NjhoNTMuODYyNFYzMjMuMjU2MzJ6TTIwMjkuMDA0OCA0MjIuODYwOGMtMzIuNTMyNDggMC01OC45NTE2OCA1LjU2MDMyLTc4LjI2NDMyIDE3LjY5NDcyLTIyLjM2NDE2IDEzLjE0ODE2LTM2LjU5Nzc2IDM0LjM4NTkyLTQyLjE4ODggNjIuNjk5NTJsNTMuMzcwODggNC41NDY1NmMzLjA0MTI4LTE0LjY2MzY4IDEwLjY3MDA4LTI1LjI4MjU2IDIyLjg2NTkyLTMyLjM1ODQgMTAuMTY4MzItNi4wNjIwOCAyMy44ODk5Mi05LjEwMzM2IDQwLjY1MjgtOS4xMDMzNiAzOS42MzkwNCAwIDU5LjQ2MzY4IDE4LjIwNjcyIDU5LjQ2MzY4IDU0LjYwOTkydjEwLjYxODg4bC01OC45NTE2OCAxLjUxNTUyYy0zOC42MjUyOCAxLjAxMzc2LTY5LjEyIDguNjAxNi05MC40NjAxNiAyMy43NTY4LTIzLjM3NzkyIDE1LjY3NzQ0LTM1LjA3MiAzOC40MzA3Mi0zNS4wNzIgNjcuNzU4MDggMCAyMS43Mzk1MiA4LjEzMDU2IDM5LjQzNDI0IDI0LjkwMzY4IDUzLjA4NDE2IDE1LjI1NzYgMTMuNjQ5OTIgMzYuNTk3NzYgMjAuNzM2IDY0LjA0MDk2IDIwLjczNiAyMy4zNzc5MiAwIDQzLjcwNDMyLTQuNTU2OCA2MC45NzkyLTEyLjY0NjRhMTA5LjMxMiAxMDkuMzEyIDAgMCAwIDM4LjExMzI4LTMxLjM0NDY0djM2LjkwNDk2aDQ5LjgwNzM2VjUyNC40OTI4YzAtMzEuODU2NjQtOC4xMzA1Ni01Ni4xMjU0NC0yMy44Nzk2OC03Mi44MDY0LTE4LjI5ODg4LTE5LjIyMDQ4LTQ2Ljc1NTg0LTI4LjgyNTYtODUuMzgxMTItMjguODI1NnogbTU1LjkwMDE2IDE0Ny42NDAzMnYxNS4xNjU0NGMwIDIwLjIyNC04LjY0MjU2IDM3LjQxNjk2LTI0LjkwMzY4IDUxLjA2Njg4LTE2LjI2MTEyIDEzLjY0OTkyLTM1LjU3Mzc2IDIwLjcyNTc2LTU4LjQzOTY4IDIwLjcyNTc2LTEzLjcyMTYgMC0yNC45MDM2OC0zLjUzMjgtMzMuMDM0MjQtMTAuMTA2ODgtOC42NDI1Ni02LjU3NDA4LTEyLjcwNzg0LTE0LjY2MzY4LTEyLjcwNzg0LTI0Ljc4MDggMC0zMi4zNTg0IDI0LjM5MTY4LTQ5LjU0MTEyIDczLjY4NzA0LTUwLjU1NDg4bDU1LjM5ODQtMS41MTU1MnpNMjMyMS43MzU2OCA0MjIuODYwOGMtMTYuMjcxMzYgMC0zMC40OTQ3MiA0LjU0NjU2LTQyLjcwMDggMTQuNjYzNjgtMTAuMTU4MDggNy4wNzU4NC0xOC44MDA2NCAxNy42OTQ3Mi0yNS4zOTUyIDMxLjg0NjR2LTM5LjQyNGgtNTMuODgyODh2MjYxLjM4NjI0aDUzLjg3MjY0VjU1Mi44MDY0YzAtMjIuNzUzMjggNi42MDQ4LTQxLjQ3MiAyMC4zMjY0LTU1LjYyMzY4IDEyLjcwNzg0LTEzLjE0ODE2IDI3LjQ0MzItMTkuNzEyIDQzLjcwNDMyLTE5LjcxMiAxMi4yMDYwOCAwIDI0LjkwMzY4IDEuNTE1NTIgMzguMTIzNTIgNS41NjAzMnYtNTMuNTk2MTZjLTkuMTU0NTYtNC41NDY1Ni0yMC44Mzg0LTYuNTc0MDgtMzQuMDQ4LTYuNTc0MDh6TTIzOTMuODk2OTYgMzMwLjM0MjR2MzYwLjk5MDcyaDEzMS4xMTI5NmM1OC40NDk5MiAwIDEwMi42NjYyNC0xNi4xNzkyIDEzMy4xNTA3Mi00OC41Mzc2IDI4Ljk3OTItMzEuMzM0NCA0My43MTQ1Ni03NS4zMzU2OCA0My43MTQ1Ni0xMzEuOTYyODggMC01Ny4xMjg5Ni0xNC4yMzM2LTEwMS4xMi00Mi43MDA4LTEzMS40NTA4OC0zMC40ODQ0OC0zMi44NzA0LTc0LjcwMDgtNDkuMDQ5Ni0xMzMuMTQwNDgtNDkuMDQ5NmgtMTMyLjEzNjk2eiBtNTUuMzk4NCA0Ny4wMTE4NGg2Ni41NzAyNGM0NS43NDIwOCAwIDc5LjI3ODA4IDEwLjYxODg4IDEwMC42Mjg0OCAzMi4zNTg0IDIwLjMyNjQgMjEuMjM3NzYgMzAuOTk2NDggNTQuNjA5OTIgMzAuOTk2NDggMTAxLjEyIDAgNDUuNTA2NTYtMTAuNjcwMDggNzguODc4NzItMzEuNTA4NDggMTAwLjYxODI0LTIxLjM0MDE2IDIxLjczOTUyLTU1LjM5ODQgMzIuODcwNC0xMDEuMTMwMjQgMzIuODcwNGgtNjUuNTU2NDh2LTI2Ni45NTY4ek0yNzU4LjI4NzM2IDMzMC4zNDI0djM2MC45OTA3MmgxNjUuNjcyOTZjMzguNjI1MjggMCA2OC42MDgtNy4wNjU2IDg5Ljk0ODE2LTIxLjIyNzUyIDI0LjkwMzY4LTE3LjIwMzIgMzcuNjExNTItNDMuNDg5MjggMzcuNjExNTItNzkuODkyNDggMC0yNC4yNjg4LTYuMTAzMDQtNDMuOTgwOC0xOC4yOTg4OC01OC42NDQ0OC0xMi4xODU2LTE0LjY2MzY4LTMwLjQ4NDQ4LTI0LjI2ODgtNTQuMzc0NC0yOC44MjU2IDE4LjI5ODg4LTYuNTc0MDggMzIuMDIwNDgtMTYuNjgwOTYgNDIuMTg4OC0yOS44MjkxMiAxMC4xNTgwOC0xNC4xNTE2OCAxNS4yMzcxMi0zMS4zNDQ2NCAxNS4yMzcxMi01MS41Njg2NCAwLTI3LjgxMTg0LTkuNjU2MzItNDkuNTUxMzYtMjguOTY4OTYtNjUuNzMwNTYtMjAuMzI2NC0xNy4xOTI5Ni00Ny43Njk2LTI1LjI4MjU2LTgzLjM1MzYtMjUuMjgyNTZoLTE2NS42NjI3MnogbTU1LjM5ODQgNDUuNDk2MzJoOTYuNTUyOTZjMjQuMzkxNjggMCA0Mi42OTA1NiA0LjA0NDggNTMuODYyNCAxMi42NDY0IDExLjE5MjMyIDguMDg5NiAxNi43NzMxMiAyMS4yMjc1MiAxNi43NzMxMiAzOS40MjQgMCAxOS4yMjA0OC01LjU4MDggMzMuMzgyNC0xNi43NjI4OCA0Mi40NzU1Mi0xMS4xODIwOCA4LjYwMTYtMjkuNDgwOTYgMTMuMTQ4MTYtNTQuODg2NCAxMy4xNDgxNmgtOTUuNTM5MlYzNzUuODM4NzJ6IG0wIDE1Mi42OTg4OGgxMDQuMTcxNTJjMjYuNDI5NDQgMCA0Ni4yNTQwOCA0LjU0NjU2IDU4Ljk1MTY4IDE0LjE1MTY4IDEyLjcwNzg0IDkuNjA1MTIgMTkuMzEyNjQgMjUuMjgyNTYgMTkuMzEyNjQgNDYuNTIwMzIgMCAyMC43MjU3Ni04LjYzMjMyIDM1Ljg5MTItMjQuOTAzNjggNDUuNDk2MzItMTMuMjA5NiA3LjA4NjA4LTMxLjUwODQ4IDExLjEzMDg4LTU0Ljg4NjQgMTEuMTMwODhoLTEwMi42NTZWNTI4LjUzNzZ6IiBmaWxsPSIjMTExMTExIiBwLWlkPSIxNzQ2Ij48L3BhdGg+PC9zdmc+", + DB.Adbpg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAycAAAMkCAYAAACiPV8HAAAACXBIWXMAACxKAAAsSgF3enRNAAAgAElEQVR4nO3d+1XcWLrw4a1Z/b89EUBHYCYC6AjsCWBkOgK7I2g6gqEjaFwJHBzBQAQDEQxE8JkI9C2VVKbMtS6Sal+eZy0f97mMjaXThh97v3tXzb/CfgjhOPCSI0+HxL0NIbzzEhnRdQjh2zO//NUT/7uH/7ObMGtuvCCAsrVx0n7h/Z/SHwQAUVmOnZv+x8N/vgqz5rkgAiBBP3lpAERoeaXv8NkPr64W/3TZ/7yIl2/96kwIs+bCCwZIgzgBIAeLgHkcMvcBc70ULT/+bAUGIAriBIBSLFZjnguYy6VoWazACBeACZk5AYDXXf4QLN0A/5XnBjAsKycA8LrDRysu3WrL9VKwXPUrLU4dA9iQlRMAGN7l0vawK0P5AKuxcgIAw/txpeV+lWV5hUWwADxg5QQAdmc5WC7MsQClEycAEJfL77FihgUojDgBgLjdfg8VqytA5sQJAKTlro+VC7MrQG7ECQCkr90Kdm5lBUid07oAIH33p4PV1fLKilgBkmLlBADydrsUK+dh1nzzvoFYiRMAKMv1UqiYVwGiIk4AoFx330PFqgoQAXECACy0qypnZlWAXREnAMBTbpdWVGz/AiYhTgCA19wthcq5pwWMRZwAAOswpwKMRpwAANv4KlSAofzNkwQAtvA+hPBXCOH/hbo6C3X1wcMENmXlBAAYmhkVYCPiBAAY011/PPGZ44mB14gTAGAq7fHEp/2Kyo2nDjwkTgCAXbjsV1QM0gPfGYgHAHbhsB+kv+kH6Q+8BcDKCQAQi8W2rzOrKVAmKycAQCz2Qgj/XjqW+MibgbJYOQEAYmY1BQpi5QQAiJnVFCiIlRMAIDXXS0cSW02BjFg5AQBS827ppK/TUFf73iDkQZwAAKl6E0L4FEL4X6irc1u+IH3iBADIwfv5NvW6ugp1deyNQprMnAAAObrtb6A/NZcC6bByAgDkqD3l6/elG+jNpUACxAkAkLN2LuVjP5fSRsqBtw3xEicAQCnaSPlvqKsLw/MQJ3ECAJTmsB+ebyPlg7cP8RAnAECp2kj5v1BXN074gjiIEwCgdHvzSx1FCuycOAEA6IgU2DFxAgDwI5ECOyJOAACeJlJgYuIEAOBlIgUmIk4AAFaziJQr96TAOMQJAMB63i3dkyJSYEDiBABgM8uXOe57hrA9cQIAsJ02Uv4X6upMpMB2xAkAwDA+hhDaeZSTUFdvPVNYnzgBABjOmxDC7yEEJ3vBBsQJAMDw3iwdP2xoHlYkTgAAxrNnaB5WJ04AAMa3GJo/NY8CzxMnAADT+dTPo3z2zOExcQIAMK12HuXfbpqHx8QJAMBuLG6aP7PVCzriBABgtz7a6gUdcQIAsHu2elG8IE4AAKJiqxdFEycAAPH56JZ5SiROAADitLhl3gWOFEOcAADEbXGB44n3RO7ECQBAGn43ME/uxAkAQDoWA/OnBubJkTgBAEjPpxCCVRSyI04AANK0ZxWF3IgTAIC0WUUhG+IEACB9VlHIgjgBAMiHVRSSJk4AAPJiFYVkiRMAgDy1qyjt7fIH3i+pECcAAPlq70X5r9vlSYU4AQDIX3u7fLuKsu9dEzNxAgBQhsN+WP7Y+yZW4gQAoBxvQgh/hbo6NyxPjMQJAEB53verKIbliYo4AQAo055heWIjTgAAyrYYlrfNi50TJwAAtMPyN26WZ9fECQAAoR+W/49tXuySOAEAYJltXuyMOAEA4CHbvNgJcQIAwFMW27w+ezpMRZwAAPCSf7u0kamIEwAAXtNe2njh0kbGJk4AAFjFuz5Qjj0txiJOAABYVTuH8leoq1NPjDGIEwAA1vXJccOMQZwAALCJ9rjhK3MoDEmcAACwqb1+DuWDJ8gQxAkAANto51D+L9TViafItsQJAABD+D3U1Zk5FLYhTgAAGMrHfpuXQGEj4gQAgCG196HcGJRnE+IEAIChvXFhI5sQJwAAjGFxYeNnT5dViRMAAMb07/mgPKxAnAAAMLaPoa7ODcrzGnECAMAU3jvJi9eIEwAAptKe5HXlJC+eI04AAJjSXr+CIlB4RJwAADC19iSv/zpqmIfECQAAu/KXQGGZOAEAYJfaQDnxBgjiBACACPzuLhSCOAEAIBIfBQriBACAWLSB4i6UgokTAABicuiyxnKJEwAAYvNOoJRJnAAAECOBUiBxAgBArNpAuXKbfDnECQAAMdvrV1AESgHECQAAsXsjUMogTgAASIFAKYA4AQAgFQIlc+IEAICUCJSMiRMAAFIjUDIlTgAASJFAyZA4AQAgVQIlM+IEAICUCZSMiBMAAFInUDIhTgAAyIFAyYA4AQAgF4tA2fdG0yROAADISRso56Gu3nqr6REnAADk5l2/giJQEiNOAADIkUBJkDgBACBXAiUx4gQAgJy1gXLqDadBnAAAkLuPoa7OvOX4iRMAAEogUBIgTgAAKEUbKJ+97XiJEwAASvLvUFfH3nicxAkAAKX5K9TVB289PuIEAIASnYW6OvDm4yJOAAAo0Zv+DhSBEhFxAgBAqd70KyguaYyEOAEAoGRukY+IOAEAoHRukY+EOAEAgO4OFIGyY+IEAAA6n9yBslviBAAA7rV3oBx5HrshTgAA4EfnjhjeDXECAAA/csTwjogTAAB47N18BYVJiRMAAHjaYairM89mOuIEAACe99EJXtMRJwAA8DIneE1EnAAAwOvaE7z2PadxiRMAAHjdmz5QnOA1InECAACraU/wOvWsxiNOAADidO29RKkdkP9c+kMYizgBAIjDXQjhSwjhnyGEv/sOfdT+bUB+HD/l+IcCAEjEbX/R31mYNVc/fMh19cFLjFo3ID9rvpX+IIYkTgAApnXdB8n5oyD50XvvJWrtgPxFCOGg9AcxJHECADC+6/nqSBckN6/+blZNUvEu1NVpmDVmUAYiTgAAxrFekPxInKTjU6irqzBrzkp/EEMQJwAAw7ntg+RsgyBZJk7SctoHykvb9FiBOAEA2M7zQ+2bqKuDfp6BdLyZv//2BC8D8lsRJwAA67tbGmo/H/j5HXsfSVpc0Oj9bUGcAACs7nJpjmSs75Db0pWu9oLGC/MnmxMnAAAvG2qO5HXdlq497yNp5k+2IE4AAJ72pQ+Siwmfjy1B6TN/sgVxAgBw77afGzjb0ReWtnTloZ0/OQkhuP9kTeIEAGA3qyQ/sqUrN5/6+ZOhD0zImjgBAEq161WSh2zpys/ZPDrHnlXKiDgBAErzdR4lu1wleZotXfl50x+mcFT6g1jV39L4MAEAttLeS/JnCOHnMGs+RBcmtnTl7DDU1UnpD2FV4gQAyFm7devXEMJ+mDWfI95eY0tX3n7vA5RX2NYFAOTocn5aUnxbt55jS1f+zvv5E8cLv8DKCQCQky/91q2jZMLElq5S7PXHC/MCcQIApK6dJ/kjhPD3MGuOEzwZyZaucrTHC1sle4FtXQBAqmI7CnhT4qQs7fHC+7Z3PU2cAACpue3nSc6Sf3Pdd9HfRPCRMJ3F8cJWUJ5gWxcAkIrL+clbs2Y/izDp+AK1TO9DXVkxe4I4AQBi10bJL/2Qey5R0q6avBUnRTudb+/iB7Z1AQCxSu044HXZ0lU2t8c/wcoJABCb5ZWSXMMkGISnvz3+swdxT5wAALEoJUpCv53nMIKPhN07sb3rnm1dAMCu5b596ylWTViwvWuJlRMAYFdui1kpeUycsMz2rp44AQCmdrt0JHBpUdJu6Wq/Q74XwUdCXNrtXW9Lfye2dQEAU7kLIXzO6jjgzVg14SnFX84YrJwAABNoo+SPEEJOlydupvvO+McUP3Qm0V7OKE4AAEbyZx8l7cD7Nw/ZpYu86qzk7V3iBAAYw9cQws9h1nwWJT8w9Mxr3sznTwolTgCAIS3uKvkQZs2NJ7ukrg5CCO+i+XiI2af+4ITiGIgHAIZw299VUvqw+0usmrCOs/mWyMJYOQEAtrEYdj8QJi/oZgjMm7COvVBXxW3vEicAwKa+9lFi2P11H/pZAljH76Guilo9sa0LAFjXdX9fSXkXKG6u2AFnttauSBYzf2LlBABYVbuF67cwaw6EyRrcCM92Dku6+0ScAACr+NLfV3Lqaa3NjfBs67SUu0/ECQDwkuv+aOBjcyUb6OYF3AjPtvZKOe1NnAAAT+lO4bKFa1tWTRhKEcPx4gQAeOj+FC62JU4YUvbHdYsTAGChvUjxn253H0hdHRuEZ2DZD8eLEwCg9We/WnLuaQzGqgljyHo4XpwAQNkWA++fDbwPqDs++DCbPw8xyXo4XpwAQLkMvI/Hqglj+pzrcLw4AYDytKsl/zDwPhLHBzO+NyGELP/9FScAUJbFasmV9z6aIu6jYOc+9tsHsyJOAKAMVkum0A0q29LFVLL791mcAED+rJZM57jfcgNTOOyPrM6GOAGAfFktmZ4tXUwtq3+/xQkA5MlqydRcushu7IW6yiZQxAkA5OXWasnOeObsyudcLmYUJwCQj8Ut71ZLptadmmTVhF3J5mhhcQIA6bsLIfzTLe87ZdWEXfuUw8WM4gQA0nYZQtgPs+bce9yRbtXksMg/O7FJPpLFCQCk67cwa46sluycVRNikfzFjOIEANKzOCL41LvbsW4bjVUTYpJ0LIsTAEjLlxDCkaH3aFg1ITaHKa+e/BTBxwAAvO5uflzorDnzrCLRrZp8LP0xEKWT+TcxEmTlBADid92vlgiTuFg1IVbJrp5YOQGAuH0Js+bYO4qMVRPidzY/yS8xVk4AIE7tNq5fhUm0rJoQu71QV8n9/SFOACA+tnHFzKoJ6UguosUJAMTFaVzxs2pCKpJbPREnABCP3+bbuFyqGC+rJqQnqZgWJwCwe3cuVUyGVRNSk9TqiTgBgN26np+oYxtX/KyakK5kolqcAMDutMcEH9jGlQwrW6QqmdUTcQIAu+GY4JR0F9q9L/0xkLQkVk/ECQBMazFf4pjgtJg1IXVJrJ6IEwCYjvmSFHWrJoelPwayEH1kixMAmIb5knRZNSEXe31sR0ucAMD4fjNfkqi6+mDVhMxEHdviBADG086X/NP9JUnz7sjNYcyrJ+IEAMZxG0I4CrPm3PNNVDc8vFf6YyBL0a6eiBMAGF47+H5g8D1hdfXWqgkZa1dPDmL844kTABjWl37FxOB72j6HEN6U/hDI2ucY/3DiBACG8+d88F2YpK2u9mP9wg0G9LH///WoiBMAGEZ747svaPNwYtWEQkT3d5Y4AYDtLE7kcuN7Drp9+B9LfwwU47ifr4qGOAGAzd05kSs7huApyZvYVk/ECQBs5roPEydy5cKFi5QpqgtixQkArE+Y5MmqCSXa6+/0iYI4AYD1XDoqOEN1deLCRQoWzdYucQIAq/sSZo0wyU03EOykNUr2LtTVUQx/fnECAKv5Mr/DhBydOjoY4pg9EScA8LrfhEmmuu8WOzoYIrmUUZwAwMvayxUNSufLu4V7O/8mjDgBgOf96nLFjNXV5/lee2Bh57NX4gQAHrsTJpnrhuBPSn8M8MCbXR8rLE4A4EeLW9+FSd4MwcPTxAkAROLO5YoFMAQPLzkMdXWwqyckTgCgI0zKYQgeXraz2RNxAgDCpBzdTfCG4OFlH/q5rMmJEwBKJ0xK0d3h4CZ4eN2beaDsgDgBoGTCpCyG4GF1Owl5cQJAqYRJSeqq/S7w+9IfA6zh3S4G48UJACUSJiXp9s47GhrWN/nqiTgBoDTCpDwntnPBRiYfjBcnAJREmJSmu9PkU+mPATY0+WC8OAGgFMKkNLZzwRAmvTFenABQAmFSpna//F7pDwG2dNgfwz0JcQJA7oRJibpThn4v/THAQCYbjBcnAORMmJTLdi4YzmRzJ+IEgFwJk1LV1cn8jgZgKHv9XUGjEycA5OqDMCmQ7VwwFnECABv6NcyaCw+vSLZzwTgmufNEnACQmzZMfIFaItu5YEyT3HkiTgDIiTApVXfZou1cMC5xAgAr+lOYFMplizCV92PfeSJOAMjBlzBrJjuHn+icuGwRJjPq6ok4ASB1bZgce4uF6rZzfSr9McCERv37VpwAkLLrKW8uJjLddq5zrwUm9W7MrV3iBIBUXfeXLH7zBot11p8gBExrtK1dP3mRAKyhvXV9lYsND0b+ovFOmBSurj7Ph3OBXWi3dp2O8fuKEwCesoiQi/7nm41uW+9u697vY+VooGgRJqXrtpSclP4YYIfezf9+3+TzwivECQAL1/3+/fPBPuF0v87VD3MBXbB86H9scmHehzE+IZKUc9u5YOeOx5j5q5p/zb+T9R/vF6BIt/3SfBskN5M/gO474B/6T3CrHAXrksXS1dWp07kgCrdh1gw+GC9OAMr0dR4ls+Yimj99dyRs+524j8/8X/wRZo2tPCXr/n/E1ywQj38MvZLttC6AsnwJIfwcZs2HqMIkzLeAXfT3lfzcf5zLvgiTwjk2GGI0+Kld4gSgDJd9lBzvZPvWOtqP7z5Svs5nYVyyiDkTiNHgcWJbF0DebufzHLPGd5xJV121q2a/e4MQpZ+H/KaXlROAfP05P7pXmJCybs5EmEC8Bl09cZQwQH7u+uN245opgXWZM4EUDHoho5UTgLxczy89FCbkwZwJxO9dfyz8IMQJQD7aE60O3JxOFro5k0MvE5JwNNQHKU4A8vCbE63IhjkTSM1gcydmTgDS59Z08tFtDzFnAml5P9RHa+UEIG3ChNyYM4EU1dUgqyfiBCBdwoS81NXZfLgWSJE4ASiYMCEvddXOTH30ViFZgwzFixOA9AgT8lJXB0PekwDsxF7/7/JWxAlAWoQJebm/aNGcCaRv661d4gQgHcKEHJ3Pv+MK5ECcABRCmJCfujp10SJk5V2/GroxcQIQP2FCfroB+E/eLGRnq9UTcQIQN2FCfrqh2b+8WcjSVqd2iROAeAkT8tNt+bjwZiFb4gQgQ8KE/NyHiZO5IF9bHSksTgDiI0zI1akb4KEIG6+eiBOAuAgT8tSdzOUGeCiDOAHIgDAhT07mgtKIE4DECRPy5GQuKNGbUFcbBYo4Adg9YUKeujBxMheUSZwAJEiYkKfuZK4zJ3NBscQJQGKECXm6PzLYyVxQrsNN/uTiBGA3hAk5c2QwEDaZOxEnANMTJuSrrs4cGQz0xAlA5IQJ+eqODBYmwII4AYiYMCFfXZg4MhhYtvbciTgBmIYwIV/uMgGes+bciTgBGJ8wIV/uMgFedrDO8xEnAOMSJuTrPkzcZQI8x8oJQCSECflyySKwGnECEAFhQr5csgis7k2/yroScQIwPGFCvoQJsD5xArADd8KEApwJE2BNK2/t+smTBRjE3fwv31lztdYvVlf7IYT2x9ul7yy9Xfd0kxBC+/t+e/DPN2HW3Hi9DKa7/f29BwqsaeXPaeIEYHuvh0m33/agD5Gj/ue9AZ/90xdd1VX7X6/7WLmYB0sbL+tGFHRh4vZ3YBMrr7ZWzb/mnyT/4zEDbORxmHR78o/6Hweb3JA7kcs+WK7mP8+ab1F8VMRHmADb+yXMmlfvRLJyArC5+zCpqw9LQZLKfvzDH8Kprq77WDlf5RMIhRAmwDCOVrmw1coJwOb+XNqmldtdD3fzSLmPFasqJaqrkxDC76U/BmAQX8Os+fDaLyROAFjFP8ypFKaujkMIf5X+GIDB3IZZs//aL+YoYQBecylMCiNMgOHt9TOZLxInALyk3d517AkVRJgA43n1SGFxAsBLTtyVUhBhAozr1csYxQkAz7kOs+bU0ymEMAHGZ+UEgI3ZzlUKYQJMQ5wAsJE/DMEXQpgA03l1KF6cAPDQbQjBdq4SCBNgei+unogTAB46duliAboLFoUJMLUXh+J/8joAWNLe4HvhgWSurs5CCB9LfwzATrx4EaOVEwAW2jtNPnsamRMmwG7Z1gXASk7daZI5YQLs3ruXPgJxAkDo7zQ58SQyJkyAWNTVs6snZk4ACLZzZaw7tvM8hHBY+qMAotHOnTx5XL2VEwC+GILPVBcmF8IEiMyzKyfiBKBshuBzdR8mL+7vBtgBcQLAk07daZKhbj+3MAFi9exxwmZOAMp1awg+Q/dh8qb0RwFE69lvnFg5ASjXsXefmbo6EiZAEp45sUucAJTp0hB8Zuqqjc3/CBMgEU9u7RInAGWyapKTumoPNfir9McAJOXJlRMzJwDl+dNN8BlxuSKQpidXTsQJQFnao4MNweegOyr4VJgAiRInAIQTRwdnwB0mQPoMxAMUrj06+LT0h5C87oSbK2ECJO5N/42WH4gTgHK4CT5190cF75X+KIAsPFo9EScAZWiPDj73rpN34qhgICNWTgAKZQg+D94jkBMrJwAF+uLCxUx07/Gy9McAZOPRiV3iBCB/vtueF+8TyIU4ASjMFxcuZsbqCZAPcQJQEBcu5suR0EAOHp08KE4A8vboJBQy0J28dutVAsmrqx9WT8QJQL7ezO/E6C7tIz+OhgZyIE4ACiJQ8mVrF5ADcQJQGIGSo+6gg+vSHwOQPHECUCCBkif31wCp+2E2UpwAlEOg5MfcCZC6Hz4niROAsgiUnLj5H8iMOAEoj0DJiwsZgZQdLn/s4gSgTAIlH1ZPgGyIE4ByCZQ8XJX+AIDELX0eEicAZRMo6bsp/QEAyft+Ypc4AUCgpGzWWDkBsiFOAAgCJXl3pT8AIGlHiw9enABM52sI4Z8R3+otUNJl9QTIgjgBGFf7He0/Qgg/h1nzIcya8/47RAIFADpmTgBG1t498WuYNW/DrDkJs+Z+aHnWfBMoAPCd07oARnC7tEpyFGbN2bO/hUBhWN88TyAH4gRge90syazZf7RK8hKBwnDMnAAps60LYEvtKslvIYS/L82SrE+gMIy3niOQsHeLD12cAKyuHW7/EkL4R79KctrHxXYECtvzboAsiBOA131dGm4/HuXSO4ECAOIE4BnX/batxRHAzw+3D0WgAFCquppfxChOAO61cyR/9tu2DvptW6sNtw9FoLCZfc8NyIE4AUq3mCP5pZ8j+TzKtq11CBTWt+eZATkQJ0CJFkHyz6U5kouonoNAYVV1ZdUEyMH81EFxApSkG2xvt8B0QbLZ8b9TESjTqKsPoa6m3b43LHEC5GD+uUScALlbBMnfvw+2D3H871QEynjaFYe6alfM/m++LaqNlDQdJfpxAzwiToAcpR0kDwmU4dXV5/5W9cOlXzvVL/JtrQOy8ZNXCWTgbv7FcQjn8x8ph8hz2j9Td8zixfJNuhFZBMrRzg8UeEkXUGfPPMN25eRzPB/sysQJkI2q+df8O0X/8UqBxNwtxUjcsyNDqqu3EQdK6N9LfIHSPbeTEMKnV/4v/xF1XD3UDcP/L64PCmAjl2HWHNnWBaRkcQ/JL0unbJUTJsEWr410syRXK4RJSHBrV6pzMgBPsq0LiN11vw3nIqnvaI/JFq/VdKsKZw/mSl7TPtfTnX3M6zMMD2RFnACxyX9+ZAgC5WV1ddLPj7xZ8z/5fuoPdWPdVrV0Pl6AFYgTIAa3fYxcFLdNaxsC5bHueZxtdWN69/HGdSnn02zpArIjToBd+fp9hWTWpHwB3m4JlM5mW7iec9A/z9gdJ/AxAqxqfqGsOAGmcr0UIyl84ZeOkgOl29rUbt/6fcBfNf65ky7GhggxgFjMV7zFCTCW2/6L5QuzIxMoMVDq6riPiHXnSl6Twr0hJxF8DACDc88JMJS7BzFiq9YulHAPShdhJyOvHPw92qDu3vHNCFEGsFuzpmpXTr712y1i/UQGxGk5RhzzG4ucV1C6rUynE51QFfPcySankAEkoWqapvs4u+/EHC39ECvAMjGSkpxWUMaZK3nNb2HWxDd3YtUEyNmsqe7j5CGxAqW77W/VFiOpyiFQNr+vZFt/hlnzeeLf83V11Z5I9jG6jwtgCC/GyUP3sXLQ/+yUEMjL4jStqz5GzIzkINVA6YbdT7a6r2Q7l2HWxHX7el21n3//G8FHAjCOteLkKd2+5oOl1RXLzJCGux9WRdp/dppWvlIKlO7zymkEH+ttmDX7O/4YflRXF74xCGRt6zh5qBtWXATLgb9EIRqXfYxYFSlVGoFyFdXnjVlTRfBRdOqq3WL27xg+FIAR/TxsnDzlfnVl8cPsCozreilErlx4yHfxB0psfo4i5LvtXBd2JwAF+GX8OHmKYIGhXPYn9wgRViNQ1vFLFP9O1dWV9wUUYkdx8pT7YNm3JQweWZ4RuelDxOlZbEagrGr3ceJ0LqAsEcXJU7oZluUVln2fTCnAYjXkxrA6oxEoq/gjzJqTnf3u3Yllf+3s9weY3i8/Rf3Qu72+7Y/zH/7n3SrLfv+j/ee3PsGSoIcRcmNQncnEf5N82erqgzABShR3nDznuWX2bmhwf2mVZd/2MHasHU7/1n8B+G1pNsRKCLsnUOLUfS47K/0xAGWKe1vXULrtC8vBctCvthw4/YQBPBUgVkFIhy1ez5n+lngncwFli3zmZCrddw5Dv0Vs+WerLoSl+Lh69LMVEHIhUJ4y7S3xwgRAnKzkPl4WKy6LFZggYJK3CI/F7Efovzh4fvsg5EqgPDRdnAgTgCBOhtSdLLYIlsUns8XWsWBof1KL4AhLqx3he3S0/71jeOFpAmXZNHEiTAAWxMlO3M/ALCxWZBYefjIscXVmca/HsuWVjG8P/ve2WMFQBMrC+HEiTACWiZNk3W81e+i1T6QPw2hIy6sUz3l6q5QtVBAXgRJGjxNhAvCQOAHgGQJlvDgRJgBP+cffPBYAntRtlTzq57gYijABeNqsuRInADxPoAxLmAC8SJwA8LJyA2XYU/2ECcCrxAkAryszUIY7AVCYAKxEnACwGlu8NiNMAFYmTgBYnUBZjzABWIs4AWA9XaB86C9Lzdl29y8JE4C1iRMA1jdrbvoVlNwDZTPCBGAj4gSAzcyaq8y3eN1s9J8SJgCbmH+zS5wAsLmcA6VbHVqPMAHY1Pz4dnECwHbyHJK/Xfs/IUwAtiZOANhefhdVUFsAABTeSURBVIGy3qqJMAEYhDgBYBh5Bcrqt8MLE4DBiBMAhtMFyupf2MdrtZUTYQIwKHECwHDq6m1/B0rqXg8sYQIwJAPxAAzuQxZfrM+aly9gFCYAQ2tX3sUJAIP6nMHjfHlmRpgAjEacADCMumqH4d9l8DSf39IlTABGJU4AGMpxJk/y6TgRJgBjmh9EIk4A2F43CP8xkyf5eN5EmACMTZwAMJhcVk3uwqz5ceVEmABMRpwAMIRc4uTHVRNhAjApcQLAdrov4HMYhG+df/8nYQIwJfecADCIHI4PXuhWToQJwLRmjXtOANhSPjfCh/n9JrPmRpgA7I44AWAbedwI37no72oRJgDTul38bj958ABsIZdB+IX/xPFhABTlZvGHtXICwGbqaj+EcJjR0/sUwccAUDRxAsCmchqEB2B3vt8vJU4A2FQug/AA7Na3xe8uTgBYX121YbLnyQEwJHECwCasmgAwlIvFryNOAFhPd7fJR08NgKGJEwDWZdUEgCE5ShiAjeV2twkAuzRrxAkAG8jvbhMAIiJOAFiHLV0ADOly+dcSJwCsw8WLAIxGnACwmro6cLcJAAO7Wv7lxAkAqzIID8DQvi3/euIEgFWZNwFgaDfLv544AeB1dfXBli4ARiBOAFibVRMAxiBOAFibOAFgeEsXMAZxAsCrui1dbzwoAAZ2+/CXEyfEqbuFGoiDVRMAxnDz8NcUJ8TqONTVkbcDURAnAIzh6uGv+ZPHTJRmzYkXAxGwpQuA8Xx7+CtbOQHgJVZNABjLo5UTcQLA0+rqbQjho6cDwEisnACwMnNfAIxn1lw8/LXFCQDPsaULgLE8OkY4iBMAXiBOABjLo2OEgzgB4ElO6QJgXI+G4YM4AeAZVk0AGNOjYfggTgB4hjgBYEyPhuGDOAHgEVu6ABifmRMAVmLVBIBxzRpxAsBKxAkAY7p87tcWJwDcq6sDW7oAGNmTqyZBnADwwLEHAsDIxAkAK7GlC4CxPXlSVxAnAHzXbena80AAGNmTFzAGcQLAkiMPA4CR3YVZ8+QFjEGcALDEvAkAY3t21SSIEwDm6uptCOGdhwHAyMQJAK8yCA/AFMQJAK8SJwBMQZwA8CrD8ACMb9aIEwBeUFdHboUHYAKXr/0W4gQAW7oAmMKLqyZBnAAgTgCYiDgB4AV1te9WeAAmIk4AeJFVEwCm8cowfBAnAMVzShcAU3h1GD6IE4DivS/9AQAwiVdXTYI4AShYd4QwAExBnADwIvMmAEzlYpXfR5wAlMvKCQBTuAuz5maV30ecAJSort6GEN559wBMYKVVkyBOAIplSxcAU1lp3iSIE4Bi2dIFwFSsnADwInECwDRmjTgB4Bl1dRBC2PN4AJjA9Tq/hTgBKI9VEwCmsvKqSRAnAEUSJwBMRZwA8CJxAsBUxAkAz+jmTd54PABM4DrMmm/r/DbiBKAsVk0AmMpaqyZBnAAUR5wAMBVxAsCL3ns8AExEnADwjLqyagLAVNaeNwniBKAoB143ABNZe9UkiBOAolg5AWAq4gSAF4kTAKYiTgB4hvtNAJjORvMmQZwAFMOqCQBTOd/09xEnAGUQJwBMZaMtXUGcABRDnAAwhbswa8QJAM+oq33zJgBMZOMwCeIEoAhWTQCYysbzJkGcABRBnAAwFSsnALzIzfAATKE9Qvhmm99HnADkrK7ehhDeeccATGCrVZMgTgCyZ0sXAFPZat4kiBOA7NnSBcAUtjpCeEGcAOTNygkAU9h61SSIE4DsHXrFAExg61WTIE4AMlZXtnQBMBUrJwC8SJwAMIXLMGu+DfH7iBOAfJk3AWAKg6yaBHECkDUrJwBMQZwA8CqXLwIwtq1vhV8mTgByVFe2dAEwhbMhfw9xApAnW7oAmMIgRwgviBOAPIkTAMZ2G2bN1ZC/hzgByJM4AWBsgw3CL4gTgDwZhgdgbIPOmwRxApAhw/AAjG/wLV1BnABkyZYuAMY2+JauIE4AsiROABjb4Fu6gjgByJI4AWBMo2zpCuIEIEuG4QEY0+lYv7Y4AciJYXgAxjfKvEkQJwDZsaULgDFdh1lzM9avL04A8rLvfQIwolEG4RfECUBerJwAMKbRtnQFcQKQnUOvFICRfB1zS1cQJwAZqStbugAY06irJkGcAGTFli4AxnInTgBYhzgBYCznYdZ8G/vpihOAfIgTAMYy6ildC+IEIB9mTgAYw22YNRdTPFlxApCPd94lACOYZNUkiBOATNSVLV0AjEWcALAWW7oAGMPod5ssEycAebByAsAYRj8+eJk4AciDOAFgaHdh1ky2pSuIE4Bs2NYFwNAmDZMgTgCy4aQuAIZ2OvUTFScAqXNSFwDDu5xyEH5BnACk7613CMDAJt/SFcQJQBaOvEYABjT5IPyCOAFIn2F4AIY0+azJgjgBSJ84AWBIO1k1CeIEIAsG4gEYyqQ3wj8kTgDS98Y7BGAgO9vSFcQJQOLqyjA8AEO5DbPmYpdPU5wApM0xwgAM5WTXT1KcAKTNvAkAQ7gLIZzv+kmKE4C0OakLgCGchVnzbddPUpwApE2cADCEnQ7CL4gTgLTZ1gXAtnZ6fPAycQKQNscIA7CtKFZNgjgBSFhdWTUBYFuXuz4+eJk4AUiXY4QB2NZZTE9QnACky8oJANtoL10UJwAMwsoJANvY+aWLD4kTgHQdeXcAbCiKSxcfEicAAFCe0xguXXxInACky8wJAJu4i+n44GXiBCBd7jgBYBPnMa6aBHECkCh3nACwuegG4RfECUCanNQFwCa+hFlzE+uTEycAadr33gDYQLSrJkGcACRLnACwrqhXTYI4AUiWbV0ArCvqVZMgTgCSZSAegHVEv2oSxAkAABQh+lWTIE4AknXo1QGwoiRWTYI4AQCA7CWxahLECUCC6spJXQCsKplVkyBOAJIkTgBYVTKrJkGcAABAtpJaNQniBCBJR14bACtIatUkiBMAAMhScqsmQZwAJMnMCQCvSW7VJIgTgCSJEwBe8keKqyZBnAAAQFbuQginqf6BxAlAeqycAPCc0zBrvqX6dMQJQHr2vDMAnpD0qkkQJwAAkI3PKa+atKqmaSL4MABYSV21W7r+52EB8MBtmDXJb/u1cgKQFvMmADzlcw5PRZwAAEDaLsOsOc/hHYoTgLRYOQHgoSQvXHyKOAFIizgBYNmXMGsucnki4gQAANJ0l9OqSRAnAACQrPbCxZucXp84AUjLkfcFwPzo4MQvXHyKOAEAgPScpH7h4lPECQAApKU9Ovgsx3cmTgAAIC1ZXLj4FHECkJZD7wugaO3RwVe5PgBxAgAAabjLedUkiBMAAEhGlkPwy8QJAADE7zrMmuyODn5InACkoq4OvCuAYmW9nWtBnACk4613BVCkP8OsuSjhDy5OAAAgXnfzWZNCiBMAAIjX59yH4JeJEwAAiFO2N8E/R5wApMNAPEBZjkv7A4sTgHQYiAcoxx9h1tyU9r7FCQAAxKW906SYIfhl4gQAAOJSxJ0mTxEnAAAQj2LuNHmKOAEAgDjclnSnyVPECUA6nNYFkLfjku40eYo4AUiH07oA8vWl5O1cC+IEAAB2667kIfhl4gQAAHar+O1cC+IEAAB252uYNeeef0ecAADAbtzNV034TpwAAMBufLCd60fiBAAAplf0ZYvPEScAADCt4i9bfI44AQCAaTmd6xniBCAdh94VQPL+sJ3reeIEAACmcR1mje1cLxAnAAAwPscGr0CcAADA+E7CrLnynF8mTgAAYFztLfCnnvHrxAkAAIzHdq41iBMAABiPW+DXIE4AAGAcboFfkzgBAIDhtccGf/Zc1yNOAABgWHfz7VysTZwAAMCwjsOsufFM1ydOAABgOO2cybnnuRlxAgAAwzBnsiVxAgAA2zNnMgBxAgAA2zNnMgBxAgAA2zFnMhBxAgAAm7s0ZzIccQIAAJsxZzIwcQIAAJs5CrPmm2c3HHECkI477wogGr+FWXPldQxLnACkwydBgDh8CbPm1LsYnjgBAIDVXYcQDMCPRJwAAMBqugF4cyajEScAALCaDy5aHJc4AQCA1/0aZs2F5zQucQIAAC9rB+DPPKPxiRMAAHjedZg1x57PNMQJAAA87XZ+0SKTEScAAPCYk7l2QJwApMMJMQDTOXYD/PTECUA6xAnANNqTuc496+mJEwAAuOdkrh0SJwAA0PnqZK7dEicAANAeGdzOmbBT4gQAgNLdzY8MdjLXzokTgHQYiAcYnjCJiDgBSIc4ARjeB0cGx0OcAABQqvbI4AtvPx7iBACAEv3qyOD4iBMAAErjLpNIiROAdJg5AdjeF3eZxKtqmqb0ZwCQjrrylzbA5i7DrDny/OJl5QQAgBJcz0/mImriBACA3F27yyQN4gQgLXfeF8BaboVJOsQJQFpcFAawurv+kkVhkghxAgBAju76FRPf1EmIOAEAIDfCJFHiBCAt7joBeJkwSZg4AUiLOAF4mTBJmDgBACAXvwqTtIkTAABy0IbJmTeZNnECkJYL7wvgEWGSCXECAEDKhElGxAkAAKkSJpkRJwBpMegJ0BEmGaqapin9GQCkpa78xQ2UTphkysoJAAApESYZEycA6bn2zoBCCZPM/VT6AwBI0DcvDSjMXQjhOMyacy8+b+IEAICYtWFy5Ob3MtjWBZAeFzECpRAmhREnAADESJgUSJwApOfGOwMyJ0wKJU4A0iNOgJy1JxIeCJMyGYgHACAW1/2KiVMJC+WGeIAUuSUeyI8wwbYuAAB27oswIdjWBZCs2xDCntcHZOBLmDXHXiTByglAsgzFAzn4U5iwzMoJQJpsfQBS92uYNWfeIsusnACkyRGbQKruhAnPsXICAMBUXK7Ii6ycAKTpwnsDEnMtTHiNlRMAAMbmDhNWYuUEIE2+8wikwh0mrMwN8QCpcks8EL/2qODP3hOrsnICkK5r7w6I2K/ChHWZOQFIly0SQIycyMXGrJwApMsnfiA2TuRiK+IEIF1WToCYfBUmbMu2LoB0+QIAiIXBdwYhTgDSZeUE2LV2vuRzmDVn3gRDECcA6bJyAuySwXcG554TgJS56wTYDTe+MwoD8QBpu/T+gIl9CbPmQJgwBnECkDZfHABTuesvVjz2xBmLOAFIm73ewBRu+21cBt8ZlTgBSNuN9weMrL2/5MDgO1NwWhdA2sQJMKY/wqw58YSZijgBSJvvZAJjaOdLPoRZc+HpMiVHCQOkznHCwLAu+zBx4AaTM3MCkD7HCQNDabdxub+EnREnAOkzdwJsq93G9Yv5EnZNnACkT5wA22hXX/fNlxADA/EA6TMUD2zKaVxERZwApM/KCbCu9lLFY6slxMZpXQA5cGIXsLqvfZgYeic6Vk4A8nAdQnjnXQIvaIfeP4dZc+YhESsD8QB5sLULeEn7DYwDYULsxAlAHgzFA89ph97bMPFNDKJnWxdAHsQJ8NBtf9O7vx9IhpUTgDz4jiiw7M9+G5cwISlO6wLIhRO7AEcEkzgrJwD5uPYuoWiL1RJhQrLMnADk48ZxwlAkqyVkw8oJQD7sLYfyWC0hK1ZOAPIhTqAcVkvIkpUTgHyIEyiD1RKy5bQugJzU1bcQwhvvFLJktYTsWTkByIvVE8hTe8v7vjAhd+IEIC/iBPJyGUL4OcyaE++VEhiIB8iLOIE83IUQTsKsOfU+KYk4AciLOIH0fQkhfA6z5pt3SWkMxAPkpq78xQ5pMvBO8aycAOTn2k3xkJR2C9epuRIwEA+QI1u7IB1f+ztLhAnFC1ZOALLUxslHrxaiZgsXPEGcAOTHygnEyylc8AID8QA5MhQPMXIKF7zCyglAngzFQzwu+yixqgmvECcAeboQJ7Bzt32UnHsVsBpxApAn36GF3XE0MGxInADkSZzAbvzZD7ybK4ENGIgHyJWheJjSlz5Kbjx12JyVE4B8tUO4h94vjOqyjxL3lcAAxAlAvi7ECYzmuh92FyUwIHECkC9zJzC8236l5MyzheGJE4B8+Y4uDEeUwAQMxAPkrK7a4dw97xg2JkpgQn/zsAGyZvUENtPeVfJHCOFAmMB0bOsCyFs7d/LRO4aVdRcodpcouqsEJiZOAPJm5QRWI0ogAmZOAHJXV+0XWm+8Z3iSmRKIiJUTgPxdue8EHhElECFxApA/lzHCPVECEXNaF0D+zJ1ACJchhF/CrNkXJhAvMycAJagrf9lTqi8hhLMwa0Q6JMC2LoAyXNraRUHak7fO++1bN148pEOcAJTB3AkluJ2vkjgOGJIlTgDKcOU9k7HrPkjMkkDixAlAGey3J0fmSSAzBuIBSlFX7erJO++bxC1ucj8zTwL5sXICUI4LcULCbN2CAogTgHK0cfLJ+yYhi1O32igxNwUFECcA5bAvn1Rc91u3zp26BWUxcwJQkrpypDCxskoCWDkBKIw4ITZWSYDvxAlAWdo4+d07Z8dul1ZJnLgFfGdbF0Bp6spf/OzKl36F5NwbAJ5i5QSgPF9DCO+9dyZi2xawMnECUJ4LccLIbpeCxLYtYGXiBKA8jhRmDIs5kjOnbQGbMnMCUKK6arfXvPHu2ZIgAQZl5QSgTO0XlB+9ezYgSIDRiBOAMl2IE9YgSIBJ2NYFUKK6ehtC+H/ePS+47iNWkACTEScApaqr9gvOd94/Sy77FRKnbAE7YVsXQLkuxEnx7voYuXAPCRADcQJQrvaL0k/ef3Gul2LEsdJAVGzrAiiZI4VLcPc9RtqfbdcCImblBKBsjhTO0+XS6ohhdiAZ4gSgbI4UzsNiq1a7MnJe+sMA0mVbF0DJHCmcqvsY6YLEIDuQBXECUDpHCqeg3aZ1JUaA3NnWBcBZCOHfxT+FeCwG2K/6EHGiFlAMcQKAL3536/pBjDhNCyiWbV0AtFu72i+I9zyJ0V33EdL9sCoC8AMrJwAEFzKOQogArEmcABD6bUXiZDN3P0RICDdCBGAztnUB0HFb/GvufgiQ+xURJ2cBDMTKCQALbovvtNuxvvWrSd9ECMB0xAkACyXFyWX/83KA3DgpC2C3xAkAC7nMSSy2X4WlP9PiZysgABEzcwLAvbpqV0/eR/pElqPjpv8RlsLDygdA4qycALBsijhZjoywtK1qYTk8voVZc/X4lwAgR+IEgGVtnOxv8ESe2xImLgBYTQjh/wOaO0WmP08QbgAAAABJRU5ErkJggg==", } # RedisCloud color: #0D6EFD diff --git a/vectordb_bench/frontend/pages/full_text_search.py b/vectordb_bench/frontend/pages/full_text_search.py new file mode 100644 index 000000000..d6023c0c8 --- /dev/null +++ b/vectordb_bench/frontend/pages/full_text_search.py @@ -0,0 +1,557 @@ +import json +import re +from pathlib import Path +from typing import Any + +import pandas as pd +import plotly.express as px +import streamlit as st + +from vectordb_bench import config +from vectordb_bench.frontend.components.check_results.footer import footer +from vectordb_bench.frontend.components.check_results.headerIcon import drawHeaderIcon +from vectordb_bench.frontend.components.check_results.nav import NavToPages +from vectordb_bench.frontend.config.styles import FAVICON + +RESULT_DIR = config.RESULTS_LOCAL_DIR / "FullTextSearch" +DATASET_ORDER = [ + "MS MARCO Small", + "MS MARCO Medium", + "MS MARCO Large", + "HotpotQA Small", + "HotpotQA Medium", + "HotpotQA Large", +] +# Published FTS results currently cover this cloud/service backend subset. +BACKEND_ORDER = ["ZillizCloud", "ElasticSearch", "OSSOpenSearch", "TurboPuffer"] +BACKEND_COLORS = { + "ZillizCloud": "#0D6EFD", + "ElasticSearch": "#04D6C8", + "OSSOpenSearch": "#61D790", + "TurboPuffer": "#FF6B2C", +} +SIZE_ORDER = ["Small", "Medium", "Large"] +FILTER_RATE_LABEL_ORDER = ["50%", "75%", "90%", "95%", "99%"] +CHART_TABS = ["QPS", "Recall", "NDCG", "MRR", "Load", "Filtered QPS"] +FILTERED_TAB = "Filtered QPS" + + +def _normalize_backend(db: str, result_file: Path) -> str: + if db == "ElasticCloud": + return "ElasticSearch" + if db: + return db + return result_file.parent.name + + +def _dataset_parts(dataset_label: str) -> tuple[str, str, str]: + if dataset_label.startswith("MS MARCO"): + family = "MS MARCO" + elif dataset_label.startswith("HotpotQA"): + family = "HotpotQA" + else: + family = dataset_label.split(" ", 1)[0] + + size = next((name for name in SIZE_ORDER if name in dataset_label), "") + return family, size, f"{family} {size}".strip() + + +def _dataset_doc_count(dataset_label: str) -> str: + match = re.search(r"\(([^)]+)\)", dataset_label) + return match.group(1) if match else "" + + +def _dataset_axis_label(dataset: str, doc_count: str) -> str: + return f"{dataset}
{doc_count}" if doc_count else dataset + + +def _dataset_axis_order(data: pd.DataFrame) -> list[str]: + labels = [] + for dataset in DATASET_ORDER: + matches = data[data["dataset"].astype(str) == dataset] + if not matches.empty: + labels.append(matches["dataset_axis_label"].iloc[0]) + return labels + + +def _filter_rate_label(value: Any) -> str: + if value is None or pd.isna(value): + return "Unfiltered" + return f"{float(value):.0%}" + + +def _backend_metric_order(data: pd.DataFrame, metric: str, ascending: bool) -> list[str]: + if data.empty or metric not in data: + return BACKEND_ORDER + + metric_data = data[["backend", metric]].dropna().copy() + if metric_data.empty: + return BACKEND_ORDER + + metric_data["backend"] = metric_data["backend"].astype(str) + scores = metric_data.groupby("backend")[metric].mean() + ordered = scores.sort_values(ascending=ascending).index.tolist() + return ordered + [backend for backend in BACKEND_ORDER if backend not in ordered] + + +def _run_context(task_label: str) -> str: + if "mathgt" in task_label: + return "Math GT" + return "Recorded" + + +def _parse_result_file(result_file: Path) -> list[dict[str, Any]]: + with result_file.open() as f: + test_result = json.load(f) + + task_label = test_result.get("task_label") or result_file.stem + rows = [] + for case_result in test_result.get("results", []): + metrics = case_result.get("metrics", {}) + task_config = case_result.get("task_config", {}) + case_config = task_config.get("case_config", {}) + custom_case = case_config.get("custom_case") or {} + additional_parameters = metrics.get("additional_parameters") or {} + fts_filter = additional_parameters.get("fts_filter") or {} + filter_rate = fts_filter.get("filter_rate", custom_case.get("filter_rate")) + dataset_label = custom_case.get("dataset_with_size_type", "") + dataset_family, dataset_size, dataset_key = _dataset_parts(dataset_label) + dataset_doc_count = _dataset_doc_count(dataset_label) + dataset_axis_label = _dataset_axis_label(dataset_key, dataset_doc_count) + backend = _normalize_backend(task_config.get("db", ""), result_file) + payload = metrics.get("payload_profile") or custom_case.get("payload_profile") or "ids_only" + row_task_label = task_config.get("task_label") or task_label + + rows.append( + { + "backend": backend, + "dataset_family": dataset_family, + "dataset_size": dataset_size, + "dataset": dataset_key, + "dataset_doc_count": dataset_doc_count, + "dataset_axis_label": dataset_axis_label, + "payload": payload, + "context": _run_context(task_label), + "task_label": row_task_label, + "filter_rate": filter_rate, + "filter_rate_label": _filter_rate_label(filter_rate), + "is_filtered": filter_rate is not None, + "load_s": metrics.get("load_duration", 0.0), + "qps": metrics.get("qps", 0.0), + "recall": metrics.get("recall", 0.0), + "ndcg": metrics.get("ndcg", 0.0), + "mrr": metrics.get("mrr", 0.0), + "p95_s": metrics.get("serial_latency_p95", 0.0), + "p99_s": metrics.get("serial_latency_p99", 0.0), + "concurrency": metrics.get("conc_num_list") or [], + "concurrent_qps": metrics.get("conc_qps_list") or [], + } + ) + + return rows + + +def _result_files(result_dir: Path) -> list[Path]: + return sorted(result_dir.rglob("result_*.json")) + + +def load_full_text_search_rows(result_dir: Path = RESULT_DIR) -> pd.DataFrame: + if not result_dir.exists(): + return pd.DataFrame() + + rows = [] + for result_file in _result_files(result_dir): + rows.extend(_parse_result_file(result_file)) + + data = pd.DataFrame(rows) + if data.empty: + return data + + data = data[data["backend"] != "Milvus"].copy() + if data.empty: + return data + + data["dataset"] = pd.Categorical(data["dataset"], DATASET_ORDER, ordered=True) + data["backend"] = pd.Categorical(data["backend"], BACKEND_ORDER, ordered=True) + data["dataset_size"] = pd.Categorical(data["dataset_size"], SIZE_ORDER, ordered=True) + data["filter_rate"] = pd.to_numeric(data["filter_rate"], errors="coerce") + return data.sort_values(["is_filtered", "dataset", "filter_rate", "backend", "payload"]).reset_index(drop=True) + + +def _filter_data(st: Any, data: pd.DataFrame) -> pd.DataFrame: + with st.sidebar: + st.header("Filters") + selected_datasets = st.multiselect( + "Dataset", + [dataset for dataset in DATASET_ORDER if dataset in set(data["dataset"].astype(str))], + default=[dataset for dataset in DATASET_ORDER if dataset in set(data["dataset"].astype(str))], + key="fts-standard-datasets", + ) + backend_options = [backend for backend in BACKEND_ORDER if backend in set(data["backend"].astype(str))] + selected_backends = st.multiselect( + "Backend", backend_options, default=backend_options, key="fts-standard-backends" + ) + payloads = sorted(data["payload"].dropna().unique().tolist()) + default_payloads = ["ids_only"] if "ids_only" in payloads else payloads + selected_payloads = st.multiselect("Payload", payloads, default=default_payloads, key="fts-standard-payloads") + + filters = ( + data["dataset"].astype(str).isin(selected_datasets) + & data["backend"].astype(str).isin(selected_backends) + & data["payload"].isin(selected_payloads) + ) + + return data[filters].copy() + + +def _filter_filtered_data(st: Any, data: pd.DataFrame) -> pd.DataFrame: + with st.sidebar: + st.header("Filters") + dataset_options = [ + family for family in ["MS MARCO", "HotpotQA"] if family in set(data["dataset_family"].astype(str)) + ] + selected_dataset = st.selectbox("Dataset", dataset_options, key="fts-filtered-dataset-family") + backend_options = [backend for backend in BACKEND_ORDER if backend in set(data["backend"].astype(str))] + selected_backends = st.multiselect( + "Backend", + backend_options, + default=backend_options, + key="fts-filtered-backends", + ) + filters = data["dataset_family"].astype(str).eq(selected_dataset) & data["backend"].astype(str).isin( + selected_backends + ) + return data[filters].copy() + + +def _draw_summary_table(st: Any, data: pd.DataFrame) -> None: + columns = [ + "dataset", + "backend", + "payload", + "load_s", + "qps", + "recall", + "ndcg", + "mrr", + "p95_s", + "p99_s", + ] + st.dataframe( + data[columns], + hide_index=True, + width="stretch", + column_config={ + "load_s": st.column_config.NumberColumn("Load s", format="%.4f"), + "qps": st.column_config.NumberColumn("QPS", format="%.4f"), + "recall": st.column_config.NumberColumn("Recall", format="%.4f"), + "ndcg": st.column_config.NumberColumn("NDCG", format="%.4f"), + "mrr": st.column_config.NumberColumn("MRR", format="%.4f"), + "p95_s": st.column_config.NumberColumn("p95 s", format="%.4f"), + "p99_s": st.column_config.NumberColumn("p99 s", format="%.4f"), + }, + ) + + +def _draw_filtered_summary_table(st: Any, data: pd.DataFrame) -> None: + concurrency_data = _concurrency_rows(data) + if concurrency_data.empty: + st.info("No filtered concurrency QPS rows found.") + return + + columns = [ + "dataset", + "backend", + "filter_rate_label", + "concurrency", + "qps", + "task_label", + ] + st.dataframe( + concurrency_data[columns], + hide_index=True, + width="stretch", + column_config={ + "filter_rate_label": "Filter", + "concurrency": st.column_config.NumberColumn("Concurrency", format="%d"), + "qps": st.column_config.NumberColumn("QPS", format="%.4f"), + }, + ) + + +def _chart_label(value: Any, metric: str) -> str: + if pd.isna(value): + return "" + + value = float(value) + abs_value = abs(value) + if metric in {"recall", "ndcg", "mrr"}: + return f"{value:.3f}" + if metric == "qps": + return f"{value / 1000:.1f}k" if abs_value >= 1000 else f"{value:.0f}" + if metric == "load_s": + if abs_value >= 3600: + return f"{value / 3600:.1f}h" + if abs_value >= 60: + return f"{value / 60:.1f}m" + return f"{value:.1f}s" + return f"{value:.1f}" + + +def _draw_metric_chart( + st: Any, + data: pd.DataFrame, + metric: str, + title: str, + backend_order: list[str] | None = None, +) -> None: + if backend_order is None: + backend_order = BACKEND_ORDER + + show_text = data["payload"].nunique() <= 1 + chart_data = data.copy() + if show_text: + chart_data["_chart_label"] = chart_data[metric].apply(lambda value: _chart_label(value, metric)) + + fig = px.bar( + chart_data, + x="dataset_axis_label", + y=metric, + color="backend", + pattern_shape="payload", + barmode="group", + category_orders={"dataset_axis_label": _dataset_axis_order(data), "backend": backend_order}, + color_discrete_map=BACKEND_COLORS, + hover_data=["dataset_doc_count", "payload", "context", "task_label"], + text="_chart_label" if show_text else None, + title=title, + ) + if show_text: + fig.update_traces( + texttemplate="%{text}", + textposition="outside", + textangle=0, + textfont={"size": 10}, + cliponaxis=False, + ) + fig.update_layout( + margin={"l": 0, "r": 0, "t": 56, "b": 12, "pad": 8}, + legend={"orientation": "h", "yanchor": "bottom", "y": 1, "xanchor": "right", "x": 1, "title": ""}, + xaxis_title="", + xaxis={"tickfont": {"size": 12}}, + uniformtext={"minsize": 10, "mode": "show"}, + ) + st.plotly_chart(fig, width="stretch", key=f"fts-{metric}") + + +def _concurrency_rows(data: pd.DataFrame) -> pd.DataFrame: + rows = [] + for row in data.to_dict("records"): + for concurrency, qps in zip(row["concurrency"], row["concurrent_qps"], strict=True): + rows.append( + { + "dataset": row["dataset"], + "dataset_family": row["dataset_family"], + "dataset_axis_label": row["dataset_axis_label"], + "dataset_doc_count": row["dataset_doc_count"], + "backend": row["backend"], + "payload": row["payload"], + "context": row["context"], + "filter_rate": row["filter_rate"], + "filter_rate_label": row["filter_rate_label"], + "concurrency": concurrency, + "qps": qps, + "task_label": row["task_label"], + } + ) + return pd.DataFrame(rows) + + +def _peak_filtered_qps_rows(data: pd.DataFrame) -> pd.DataFrame: + concurrency_data = _concurrency_rows(data) + if concurrency_data.empty: + return concurrency_data + + concurrency_data["qps"] = pd.to_numeric(concurrency_data["qps"], errors="coerce") + concurrency_data = concurrency_data.dropna(subset=["qps"]) + if concurrency_data.empty: + return concurrency_data + + peak_indices = concurrency_data.groupby(["backend", "filter_rate_label"], sort=False, observed=True)["qps"].idxmax() + return concurrency_data.loc[peak_indices].reset_index(drop=True) + + +def _draw_concurrency_chart(st: Any, data: pd.DataFrame) -> None: + concurrency_data = _concurrency_rows(data) + if concurrency_data.empty: + return + + fig = px.line( + concurrency_data, + x="concurrency", + y="qps", + color="backend", + line_dash="dataset_axis_label", + symbol="payload", + markers=True, + category_orders={"dataset_axis_label": _dataset_axis_order(data), "backend": BACKEND_ORDER}, + color_discrete_map=BACKEND_COLORS, + hover_data=["dataset", "dataset_doc_count", "payload", "context", "task_label"], + title="Concurrent Search QPS", + ) + fig.update_layout( + margin={"l": 0, "r": 0, "t": 48, "b": 12, "pad": 8}, + legend={"orientation": "h", "yanchor": "bottom", "y": 1, "xanchor": "right", "x": 1, "title": ""}, + ) + fig.update_xaxes(title_text="Concurrency") + fig.update_yaxes(title_text="QPS") + st.plotly_chart(fig, width="stretch", key="fts-concurrency-qps") + + +def _draw_filtered_qps_tab(st: Any, data: pd.DataFrame) -> None: + filtered_data = data[(data["payload"] == "ids_only") & data["filter_rate"].notna()].copy() + peak_data = _peak_filtered_qps_rows(filtered_data) + if peak_data.empty: + st.info("No filtered concurrency QPS rows found.") + return + + selected_family = str(peak_data["dataset_family"].iloc[0]) + peak_data["_chart_label"] = peak_data["qps"].apply(lambda value: _chart_label(value, "qps")) + backend_order = _backend_metric_order(peak_data, "qps", ascending=False) + filter_rate_order = [label for label in FILTER_RATE_LABEL_ORDER if label in set(peak_data["filter_rate_label"])] + + fig = px.bar( + peak_data, + x="filter_rate_label", + y="qps", + color="backend", + barmode="group", + category_orders={ + "filter_rate_label": filter_rate_order, + "backend": backend_order, + }, + color_discrete_map=BACKEND_COLORS, + hover_data=[ + "dataset", + "dataset_doc_count", + "payload", + "concurrency", + "task_label", + ], + text="_chart_label", + title=f"{selected_family} Peak Filtered Concurrent Search QPS", + ) + fig.update_traces( + texttemplate="%{text}", + textposition="outside", + textangle=0, + textfont={"size": 10}, + cliponaxis=False, + ) + fig.update_layout( + margin={"l": 0, "r": 0, "t": 56, "b": 12, "pad": 8}, + legend={"orientation": "h", "yanchor": "bottom", "y": 1, "xanchor": "right", "x": 1, "title": ""}, + xaxis_title="Filter rate", + yaxis_title="QPS", + uniformtext={"minsize": 10, "mode": "show"}, + ) + fig.update_xaxes(type="category", categoryorder="array", categoryarray=filter_rate_order) + st.plotly_chart(fig, width="stretch", key=f"fts-filtered-concurrency-qps-{selected_family}") + + +def main(): + st.set_page_config( + page_title="Full Text Search Cloud Results", + page_icon=FAVICON, + layout="wide", + ) + + drawHeaderIcon(st) + NavToPages(st) + + st.title("Full Text Search Cloud Results") + st.caption("Published FTS results for Zilliz Cloud, ElasticSearch, OpenSearch, and TurboPuffer.") + + data = load_full_text_search_rows() + if data.empty: + st.warning("No FullTextSearch result JSONs found.") + footer(st.container()) + return + + normal_data = data[~data["is_filtered"]].copy() + filtered_data = data[data["is_filtered"]].copy() + active_tab = st.session_state.get("fts-chart-tabs", CHART_TABS[0]) + if active_tab == FILTERED_TAB: + shown_data = normal_data + shown_filtered_data = _filter_filtered_data(st, filtered_data) if not filtered_data.empty else filtered_data + else: + shown_data = _filter_data(st, normal_data) if not normal_data.empty else normal_data + shown_filtered_data = filtered_data + + if shown_data.empty and shown_filtered_data.empty: + st.warning("No rows match the selected filters.") + footer(st.container()) + return + + if active_tab == FILTERED_TAB and not shown_filtered_data.empty: + _draw_filtered_summary_table(st, shown_filtered_data) + elif not shown_data.empty: + _draw_summary_table(st, shown_data) + + chart_tabs = st.tabs(CHART_TABS, default=active_tab, key="fts-chart-tabs", on_change="rerun") + with chart_tabs[0]: + qps_data = shown_data + if qps_data.empty: + st.info("No standard FTS rows match the selected filters.") + else: + _draw_metric_chart( + st, + qps_data, + "qps", + "Search QPS", + _backend_metric_order(qps_data, "qps", ascending=False), + ) + with chart_tabs[1]: + recall_data = shown_data[shown_data["payload"] == "ids_only"] + _draw_metric_chart( + st, + recall_data, + "recall", + "Semantic Recall", + _backend_metric_order(recall_data, "recall", ascending=False), + ) + with chart_tabs[2]: + ndcg_data = shown_data[shown_data["payload"] == "ids_only"] + _draw_metric_chart( + st, + ndcg_data, + "ndcg", + "NDCG", + _backend_metric_order(ndcg_data, "ndcg", ascending=False), + ) + with chart_tabs[3]: + mrr_data = shown_data[shown_data["payload"] == "ids_only"] + _draw_metric_chart( + st, + mrr_data, + "mrr", + "MRR", + _backend_metric_order(mrr_data, "mrr", ascending=False), + ) + with chart_tabs[4]: + load_data = shown_data[shown_data["payload"] == "ids_only"] + _draw_metric_chart( + st, + load_data, + "load_s", + "Load Duration", + _backend_metric_order(load_data, "load_s", ascending=True), + ) + with chart_tabs[5]: + _draw_filtered_qps_tab(st, shown_filtered_data) + + footer(st.container()) + + +if __name__ == "__main__": + main() diff --git a/vectordb_bench/frontend/pages/qps_recall.py b/vectordb_bench/frontend/pages/qps_recall.py index 27f9c4691..75dcd570b 100644 --- a/vectordb_bench/frontend/pages/qps_recall.py +++ b/vectordb_bench/frontend/pages/qps_recall.py @@ -41,9 +41,15 @@ def main(): def case_results_filter(case_result: CaseResult) -> bool: case = case_result.task_config.case_config.case - return case.label == CaseLabel.Performance and case.filters.type == FilterOp.NonFilter - - default_selected_task_labels = ["standard_2025"] + # Include both vector performance cases and FTS cases + return ( + case.label == CaseLabel.Performance and case.filters.type == FilterOp.NonFilter + ) or case.label == CaseLabel.FullTextSearchPerformance + + default_selected_task_labels = ["standard_20260403", "standard_20250519"] + # Filter defaults to only include labels that exist in results + available_labels = {r.task_label for r in allResults} + default_selected_task_labels = [l for l in default_selected_task_labels if l in available_labels] shownData, failedTasks, showCaseNames = getshownData( resultSelectorContainer, allResults, diff --git a/vectordb_bench/frontend/pages/results.py b/vectordb_bench/frontend/pages/results.py index a146f2fdc..216029bb1 100644 --- a/vectordb_bench/frontend/pages/results.py +++ b/vectordb_bench/frontend/pages/results.py @@ -32,7 +32,7 @@ def main(): st.caption( "Choose your desired test results to display from the sidebar. " "For your reference, we've included two standard benchmarks tested by our team. " - "Note that `standard_2025` was tested in 2025; the others in 2023. " + "Note that `standard_20260403` is the latest benchmark; the others were tested in 2023-2025. " "Unless explicitly labeled as distributed multi-node, test with single-node mode by default." ) st.caption("We welcome community contributions for better results, parameter configurations, and optimizations.") diff --git a/vectordb_bench/frontend/pages/run_test.py b/vectordb_bench/frontend/pages/run_test.py index e4472e767..64115ff17 100644 --- a/vectordb_bench/frontend/pages/run_test.py +++ b/vectordb_bench/frontend/pages/run_test.py @@ -1,5 +1,4 @@ import streamlit as st -from vectordb_bench.frontend.components.run_test.autoRefresh import autoRefresh from vectordb_bench.frontend.components.run_test.caseSelector import caseSelector from vectordb_bench.frontend.components.run_test.dbConfigSetting import dbConfigSettings from vectordb_bench.frontend.components.run_test.dbSelector import dbSelector @@ -57,9 +56,6 @@ def main(): # nav to results NavToResults(st, key="footer-nav-to-results") - # autofresh - autoRefresh() - if __name__ == "__main__": main() diff --git a/vectordb_bench/frontend/vdbbench.py b/vectordb_bench/frontend/vdbbench.py index 860467734..90769019c 100644 --- a/vectordb_bench/frontend/vdbbench.py +++ b/vectordb_bench/frontend/vdbbench.py @@ -1,10 +1,33 @@ import streamlit as st +from tornado.iostream import StreamClosedError +from tornado.websocket import WebSocketClosedError, WebSocketProtocol13 + from vectordb_bench.frontend.components.check_results.headerIcon import drawHeaderIcon from vectordb_bench.frontend.components.custom.initStyle import initStyle from vectordb_bench.frontend.components.welcome.explainPrams import explainPrams from vectordb_bench.frontend.components.welcome.welcomePrams import welcomePrams from vectordb_bench.frontend.config.styles import FAVICON, PAGE_TITLE +# Consume expected WS-close errors on streamlit's fire-and-forget writes +# (streamlit#9787, unfixed upstream). +_orig_write_message = WebSocketProtocol13.write_message + + +def _write_message_with_consumer(self, message, binary=False): + task = _orig_write_message(self, message, binary=binary) + + def _consume(t): + exc = t.exception() + if exc is None or isinstance(exc, (WebSocketClosedError, StreamClosedError)): + return + t.get_loop().call_exception_handler({"message": "websocket write failed", "exception": exc, "task": t}) + + task.add_done_callback(_consume) + return task + + +WebSocketProtocol13.write_message = _write_message_with_consumer + def main(): st.set_page_config( diff --git a/vectordb_bench/interface.py b/vectordb_bench/interface.py index 42dc876b0..78e6958a2 100644 --- a/vectordb_bench/interface.py +++ b/vectordb_bench/interface.py @@ -2,20 +2,17 @@ import logging import multiprocessing as mp import pathlib -import signal import traceback import uuid -from collections.abc import Callable from enum import Enum from multiprocessing.connection import Connection -import psutil - from . import config from .backend.assembler import Assembler, FilterNotSupportedError from .backend.data_source import DatasetSource from .backend.result_collector import ResultCollector from .backend.task_runner import TaskRunner +from .backend.utils import kill_proc_tree from .metric import Metric from .models import ( CaseResult, @@ -46,14 +43,18 @@ def __init__(self): # set default data source by ENV if config.DATASET_SOURCE.upper() == "ALIYUNOSS": self.dataset_source: DatasetSource = DatasetSource.AliyunOSS + elif config.DATASET_SOURCE.upper() == "IR_DATASETS": + self.dataset_source: DatasetSource = DatasetSource.IR_DATASETS else: self.dataset_source: DatasetSource = DatasetSource.S3 def set_drop_old(self, drop_old: bool): self.drop_old = drop_old - def set_download_address(self, use_aliyun: bool): - if use_aliyun: + def set_download_address(self, use_aliyun: bool, use_ir_datasets: bool = False): + if use_ir_datasets: + self.dataset_source = DatasetSource.IR_DATASETS + elif use_aliyun: self.dataset_source = DatasetSource.AliyunOSS else: self.dataset_source = DatasetSource.S3 @@ -166,7 +167,7 @@ def _async_task_v2(self, running_task: TaskRunner, send_conn: Connection) -> Non return c_results = [] - latest_runner, cached_load_duration = None, None + latest_loaded_reuse_key, cached_load_duration = None, None for idx, runner in enumerate(running_task.case_runners): case_res = CaseResult( metrics=Metric(), @@ -174,7 +175,10 @@ def _async_task_v2(self, running_task: TaskRunner, send_conn: Connection) -> Non ) drop_old = TaskStage.DROP_OLD in runner.config.stages - if (latest_runner and runner == latest_runner) or not self.drop_old: + reuse_key = runner.load_reuse_key() + if reuse_key is not None and reuse_key == latest_loaded_reuse_key: + drop_old = False + if not self.drop_old: drop_old = False num_cases = running_task.num_cases() try: @@ -185,14 +189,12 @@ def _async_task_v2(self, running_task: TaskRunner, send_conn: Connection) -> Non f"result={case_res.metrics}, label={case_res.label}" ) - # cache the latest succeeded runner - latest_runner = runner - - # cache the latest drop_old=True load_duration of the latest succeeded runner - cached_load_duration = case_res.metrics.load_duration if drop_old else cached_load_duration + if drop_old and TaskStage.LOAD in runner.config.stages and reuse_key is not None: + latest_loaded_reuse_key = reuse_key + cached_load_duration = case_res.metrics.load_duration # use the cached load duration if this case didn't drop the existing collection - if not drop_old: + if not drop_old and reuse_key is not None and reuse_key == latest_loaded_reuse_key: case_res.metrics.load_duration = cached_load_duration if cached_load_duration else 0.0 except (LoadTimeoutError, PerformanceTimeoutError) as e: log.warning(f"[{idx+1}/{num_cases}] case {runner.display()} failed to run, reason={e}") @@ -240,7 +242,7 @@ def _clear_running_task(self): for r in self.running_task.case_runners: r.stop() - self.kill_proc_tree(timeout=5) + kill_proc_tree() self.running_task = None if self.receive_conn: @@ -261,29 +263,5 @@ def _run_async(self, conn: Connection) -> bool: return True - def kill_proc_tree( - self, - sig: int = signal.SIGTERM, - timeout: float | None = None, - on_terminate: Callable | None = None, - ): - """Kill a process tree (including grandchildren) with signal - "sig" and return a (gone, still_alive) tuple. - "on_terminate", if specified, is a callback function which is - called as soon as a child terminates. - """ - children = psutil.Process().children(recursive=True) - for p in children: - try: - log.warning(f"sending SIGTERM to child process: {p}") - p.send_signal(sig) - except psutil.NoSuchProcess: - pass - _, alive = psutil.wait_procs(children, timeout=timeout, callback=on_terminate) - - for p in alive: - log.warning(f"force killing child process: {p}") - p.kill() - benchmark_runner = BenchMarkRunner() diff --git a/vectordb_bench/metric.py b/vectordb_bench/metric.py index 3634b2114..540cefd28 100644 --- a/vectordb_bench/metric.py +++ b/vectordb_bench/metric.py @@ -24,11 +24,21 @@ class Metric: serial_latency_p95: float = 0.0 recall: float = 0.0 ndcg: float = 0.0 + mrr: float = 0.0 conc_num_list: list[int] = field(default_factory=list) conc_qps_list: list[float] = field(default_factory=list) conc_latency_p99_list: list[float] = field(default_factory=list) conc_latency_p95_list: list[float] = field(default_factory=list) conc_latency_avg_list: list[float] = field(default_factory=list) + payload_profile: str = "ids_only" + payload_estimated_bytes_per_query: int = 0 + + inserted_count: int = 0 + insert_rows_per_second: float = 0.0 + insert_completion_seconds: float = 0.0 + searchable_after_insert_seconds: float = 0.0 + indexed_after_searchable_seconds: float = 0.0 + additional_parameters: dict = field(default_factory=dict) # for streaming cases st_ideal_insert_duration: int = 0 @@ -110,3 +120,52 @@ def calc_ndcg(ground_truth: list[int], got: list[int], ideal_dcg: float) -> floa idx = ground_truth.index(got_id) dcg += 1 / np.log2(idx + 2) return dcg / ideal_dcg + + +def _positive_fts_qrels(ground_truth: dict[str, int] | list[int] | list[str]) -> dict[str, int]: + if isinstance(ground_truth, dict): + return {str(doc_id): int(rel) for doc_id, rel in ground_truth.items() if int(rel) > 0} + return {str(doc_id): 1 for doc_id in ground_truth} + + +def calc_recall_fts(k: int, ground_truth: dict[str, int] | list[int] | list[str], got: list[int] | list[str]) -> float: + gt = _positive_fts_qrels(ground_truth) + if not gt or k <= 0: + return 0.0 + got_set = {str(doc_id) for doc_id in got[:k]} + return len(set(gt) & got_set) / len(gt) + + +def calc_mrr_fts(k: int, ground_truth: dict[str, int] | list[int] | list[str], got: list[int] | list[str]) -> float: + gt = _positive_fts_qrels(ground_truth) + if not gt or k <= 0: + return 0.0 + for rank, doc_id in enumerate(got[:k], start=1): + if str(doc_id) in gt: + return 1 / rank + return 0.0 + + +def calc_ndcg_fts(k: int, ground_truth: dict[str, int] | list[int] | list[str], got: list[int] | list[str]) -> float: + gt = _positive_fts_qrels(ground_truth) + if not gt or k <= 0: + return 0.0 + + dcg = 0.0 + seen = set() + for rank, raw_doc_id in enumerate(got[:k], start=1): + doc_id = str(raw_doc_id) + if doc_id in seen: + continue + seen.add(doc_id) + rel = gt.get(doc_id, 0) + if rel > 0: + dcg += rel / np.log2(rank + 1) + + ideal_dcg = 0.0 + for rank, rel in enumerate(sorted(gt.values(), reverse=True)[:k], start=1): + ideal_dcg += rel / np.log2(rank + 1) + + if ideal_dcg == 0: + return 0.0 + return dcg / ideal_dcg diff --git a/vectordb_bench/models.py b/vectordb_bench/models.py index 88b162ae3..d427d4f58 100644 --- a/vectordb_bench/models.py +++ b/vectordb_bench/models.py @@ -1,8 +1,9 @@ import logging import pathlib +from dataclasses import asdict from datetime import date, datetime from enum import Enum, StrEnum -from typing import Self +from typing import Any, ClassVar, Self import ujson @@ -17,6 +18,7 @@ DBConfig, EmptyDBCaseConfig, ) +from .backend.clients.api import IndexType from .base import BaseModel from .metric import Metric @@ -44,6 +46,16 @@ class CaseConfigParamType(Enum): """ IndexType = "IndexType" + drop_ratio_search = "drop_ratio_search" + drop_ratio_build = "drop_ratio_build" + bm25_k1 = "bm25_k1" + bm25_b = "bm25_b" + inverted_index_algo = "inverted_index_algo" + analyzer_tokenizer = "analyzer_tokenizer" + analyzer_enable_lowercase = "analyzer_enable_lowercase" + analyzer_max_len = "analyzer_max_len" + analyzer_max_token_length = "analyzer_max_token_length" # noqa: S105 + analyzer_stop_words = "analyzer_stop_words" index = "index" M = "M" EFConstruction = "efConstruction" @@ -149,12 +161,18 @@ class CaseConfigParamType(Enum): dataset_with_size_type = "dataset_with_size_type" filter_rate = "filter_rate" + payload_profile = "payload_profile" insert_rate = "insert_rate" search_stages = "search_stages" concurrencies = "concurrencies" optimize_after_write = "optimize_after_write" read_dur_after_write = "read_dur_after_write" + # PolarDB parameters + insert_workers = "insert_workers" + post_load_index = "post_load_index" + pq_nbits = "pq_nbits" + # Lindorm parameters efSearch = "efSearch" pq_m = "pq_m" @@ -168,6 +186,15 @@ class CaseConfigParamType(Enum): exbits = "exbits" number_of_regions = "number_of_regions" + # ADBPG parameters + hnsw_m = "hnsw_m" + algorithm = "algorithm" + rabitq_bits = "rabitq_bits" + quantize_rescore_amp = "quantize_rescore_amp" + nova_adaptive_gamma = "nova_adaptive_gamma" + max_scan_points = "max_scan_points" + auto_reduction = "auto_reduction" + class CustomizedCase(BaseModel): pass @@ -177,6 +204,7 @@ class ConcurrencySearchConfig(BaseModel): num_concurrency: list[int] = config.NUM_CONCURRENCY concurrency_duration: int = config.CONCURRENCY_DURATION concurrency_timeout: int = config.CONCURRENCY_TIMEOUT + serial_cooldown: float = config.SERIAL_COOLDOWN class CaseConfig(BaseModel): @@ -238,6 +266,7 @@ class TaskConfig(BaseModel): db_case_config: DBCaseConfig case_config: CaseConfig stages: list[TaskStage] = ALL_TASK_STAGES + load_concurrency: int = config.LOAD_CONCURRENCY @property def db_name(self): @@ -270,6 +299,68 @@ class TestResult(BaseModel): file_fmt: str = "result_{}_{}_{}.json" # result_20230718_statndard_milvus.json timestamp: float = 0.0 + sensitive_output_fields: ClassVar[set[str]] = {"api_key", "password", "token"} + + @classmethod + def _redact_sensitive_fields(cls, value: Any) -> Any: + if isinstance(value, dict): + return { + key: ( + "**********" + if key.lower() in cls.sensitive_output_fields and item + else cls._redact_sensitive_fields(item) + ) + for key, item in value.items() + } + if isinstance(value, list): + return [cls._redact_sensitive_fields(item) for item in value] + return value + + @staticmethod + def _output_metrics_for_case(case_result: CaseResult) -> dict: + metrics = asdict(case_result.metrics) + case_id = case_result.task_config.case_config.case_id + + if case_id == CaseType.CloudInsertCase: + return { + "inserted_count": metrics["inserted_count"], + "insert_rows_per_second": metrics["insert_rows_per_second"], + "insert_completion_seconds": metrics["insert_completion_seconds"], + "searchable_after_insert_seconds": metrics["searchable_after_insert_seconds"], + "indexed_after_searchable_seconds": metrics["indexed_after_searchable_seconds"], + "additional_parameters": metrics["additional_parameters"], + } + + if case_id == CaseType.CloudColdLatencyCase: + return { + "insert_duration": metrics["insert_duration"], + "optimize_duration": metrics["optimize_duration"], + "load_duration": metrics["load_duration"], + "payload_profile": metrics["payload_profile"], + "payload_estimated_bytes_per_query": metrics["payload_estimated_bytes_per_query"], + "cold_latency": metrics["additional_parameters"].get("cold_latency", {}), + } + + return metrics + + @staticmethod + def _output_case_config_for_case(case_result: CaseResult) -> dict: + case_config = case_result.task_config.case_config + + if case_config.case_id in {CaseType.CloudInsertCase, CaseType.CloudColdLatencyCase}: + return { + "case_id": case_config.case_id.value, + "custom_case": case_config.custom_case, + } + + return case_config.model_dump(mode="json") + + def model_dump_for_output(self) -> dict: + output = self.model_dump(mode="json", serialize_as_any=True) + for idx, case_result in enumerate(self.results): + output["results"][idx]["metrics"] = self._output_metrics_for_case(case_result) + output["results"][idx]["task_config"]["case_config"] = self._output_case_config_for_case(case_result) + return self._redact_sensitive_fields(output) def flush(self): db2case = self.get_db_results() @@ -308,8 +399,8 @@ def write_db_file(self, result_dir: pathlib.Path, partial: Self, db: str): log.info(f"write results to disk {result_file}") with pathlib.Path(result_file).open("w") as f: - b = partial.model_dump_json(exclude={"db_config": {"password", "api_key"}}) - f.write(b) + f.write(ujson.dumps(partial.model_dump_for_output(), indent=2)) + f.write("\n") def get_case_config(case_config: CaseConfig) -> dict[CaseConfig]: if case_config["case_id"] in {6, 7, 8, 9, 12, 13, 14, 15}: @@ -340,12 +431,14 @@ def read_file(cls, full_path: pathlib.Path, trans_unit: bool = False) -> Self: task_config = case_result.get("task_config") case_config = task_config.get("case_config") db = DB(task_config.get("db")) - task_config["db_config"] = db.config_cls(**task_config["db_config"]) - # Safely instantiate DBCaseConfig (fallback to EmptyDBCaseConfig on None) raw_case_cfg = task_config.get("db_case_config") or {} index_value = raw_case_cfg.get("index", None) + + # Handle FTS cases + if case_config.get("case_id") == CaseType.FTSBm25Performance.value: + index_value = IndexType.FTS try: task_config["db_case_config"] = db.case_config_cls(index_type=index_value)(**raw_case_cfg) except Exception: @@ -354,28 +447,32 @@ def read_file(cls, full_path: pathlib.Path, trans_unit: bool = False) -> Self: task_config["case_config"] = cls.get_case_config(case_config=case_config) case_result["task_config"] = task_config - - if trans_unit: - cur_max_count = case_result["metrics"]["max_load_count"] - case_result["metrics"]["max_load_count"] = ( - cur_max_count / 1000 if int(cur_max_count) > 0 else cur_max_count - ) - - cur_latency = case_result["metrics"]["serial_latency_p99"] - case_result["metrics"]["serial_latency_p99"] = ( - cur_latency * 1000 if cur_latency > 0 else cur_latency - ) - - # Handle P95 latency for backward compatibility with existing result files - if "serial_latency_p95" in case_result["metrics"]: - cur_latency_p95 = case_result["metrics"]["serial_latency_p95"] - case_result["metrics"]["serial_latency_p95"] = ( + metrics = case_result.get("metrics") + if ( + metrics + and CaseType(case_config.get("case_id")) == CaseType.CloudColdLatencyCase + and "cold_latency" in metrics + ): + metrics.setdefault("additional_parameters", {})["cold_latency"] = metrics.pop("cold_latency") + + if trans_unit and metrics: + if "max_load_count" in metrics: + cur_max_count = metrics["max_load_count"] + metrics["max_load_count"] = cur_max_count / 1000 if int(cur_max_count) > 0 else cur_max_count + + if "serial_latency_p99" in metrics: + cur_latency = metrics["serial_latency_p99"] + metrics["serial_latency_p99"] = cur_latency * 1000 if cur_latency > 0 else cur_latency + + # Handle P95 latency for backward compatibility with existing result files. + if "serial_latency_p95" in metrics: + cur_latency_p95 = metrics["serial_latency_p95"] + metrics["serial_latency_p95"] = ( cur_latency_p95 * 1000 if cur_latency_p95 > 0 else cur_latency_p95 ) - else: - # Default to 0 for older result files that don't have P95 data - case_result["metrics"]["serial_latency_p95"] = 0.0 - return TestResult.validate(test_result) + elif "serial_latency_p99" in metrics: + metrics["serial_latency_p95"] = 0.0 + return TestResult.model_validate(test_result) def display(self, dbs: list[DB] | None = None): filter_list = dbs if dbs and isinstance(dbs, list) else None diff --git a/vectordb_bench/restful/format_res.py b/vectordb_bench/restful/format_res.py index 326986319..c4af0d579 100644 --- a/vectordb_bench/restful/format_res.py +++ b/vectordb_bench/restful/format_res.py @@ -31,11 +31,14 @@ class FormatResult(BaseModel): load_duration: int = 0 qps: float = 0 serial_latency_p99: float = 0 + serial_latency_p95: float = 0 recall: float = 0 ndcg: float = 0 + mrr: float = 0 conc_num_list: list[int] = [] conc_qps_list: list[float] = [] conc_latency_p99_list: list[float] = [] + conc_latency_p95_list: list[float] = [] conc_latency_avg_list: list[float] = [] @@ -66,7 +69,7 @@ def format_results(test_results: list[TestResult], task_label: str) -> list[dict params=task_config.db_case_config.model_dump(), case_name=case.name, dataset=dataset.full_name, - dim=dataset.dim, + dim=getattr(dataset, "dim", 0), filter_type=filter_.type.name, filter_rate=filter_.filter_rate, k=task_config.case_config.k, diff --git a/vectordb_bench/results/ElasticCloud/result_20260403_standard_elasticcloud.json b/vectordb_bench/results/ElasticCloud/result_20260403_standard_elasticcloud.json new file mode 100644 index 000000000..2ec557926 --- /dev/null +++ b/vectordb_bench/results/ElasticCloud/result_20260403_standard_elasticcloud.json @@ -0,0 +1,1556 @@ +{ + "run_id": "c11e83b51ff14060a08f06d58f801214", + "task_label": "standard_20260403", + "results": [ + { + "metrics": { + "max_load_count": 0, + "insert_duration": 3846.0365, + "optimize_duration": 1110.4687, + "load_duration": 4956.5052, + "qps": 2030.4249, + "serial_latency_p99": 0.0106, + "serial_latency_p95": 0.0073, + "recall": 0.925, + "ndcg": 0.9306, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 139.9315, + 742.8751, + 1246.354, + 1773.6358, + 1944.4702, + 1997.4895, + 2030.4249, + 2030.061 + ], + "conc_latency_p99_list": [ + 0.011573358500827451, + 0.01055288627227128, + 0.013919824328950206, + 0.021638741448914516, + 0.035741090469564335, + 0.045256564997544046, + 0.06317665028094777, + 0.08284782179980536 + ], + "conc_latency_p95_list": [ + 0.00789401560141414, + 0.007500608301234024, + 0.010187717052576773, + 0.017150824350028415, + 0.026996734849308254, + 0.03459080500033451, + 0.04788784860138549, + 0.0629504940006882 + ], + "conc_latency_avg_list": [ + 0.0071435065296698895, + 0.006726900525604191, + 0.008019201478369918, + 0.011268319014980386, + 0.01541493476050156, + 0.02000300411234457, + 0.029459945652882687, + 0.039275078762117686 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "8c60g-force_merge", + "version": "8.17", + "note": "", + "cloud_id": "**********", + "password": "**********" + }, + "db_case_config": { + "element_type": "float", + "index": "hnsw", + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "merge_max_thread_count": 8, + "use_rescore": false, + "oversample_ratio": 2.0, + "use_routing": false, + "use_force_merge": true, + "metric_type": "COSINE", + "efConstruction": 256, + "M": 16, + "num_candidates": 200 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 3846.0365, + "optimize_duration": 1110.4687, + "load_duration": 4956.5052, + "qps": 1804.8996, + "serial_latency_p99": 0.0123, + "serial_latency_p95": 0.0079, + "recall": 0.9365, + "ndcg": 0.9405, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 128.0906, + 646.8272, + 1197.2061, + 1629.7064, + 1728.435, + 1774.0467, + 1804.8996, + 1802.14 + ], + "conc_latency_p99_list": [ + 0.0134131232793152, + 0.012170731302467179, + 0.01435177448063773, + 0.023948891401232696, + 0.038592138251187846, + 0.04927967675830586, + 0.06325175963895165, + 0.08297362611905544 + ], + "conc_latency_p95_list": [ + 0.008920073449553456, + 0.008726374499019585, + 0.011022335800225845, + 0.019354423999175197, + 0.029856205349278752, + 0.03790496999936294, + 0.05230563919976702, + 0.06659373179936665 + ], + "conc_latency_avg_list": [ + 0.007804807226078364, + 0.007726593180097387, + 0.008348314213725788, + 0.01226467845434359, + 0.017345920270812453, + 0.022531855009387067, + 0.033063582489267676, + 0.04425664062925568 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "8c60g-force_merge", + "version": "8.17", + "note": "", + "cloud_id": "**********", + "password": "**********" + }, + "db_case_config": { + "element_type": "float", + "index": "hnsw", + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "merge_max_thread_count": 8, + "use_rescore": false, + "oversample_ratio": 2.0, + "use_routing": false, + "use_force_merge": true, + "metric_type": "COSINE", + "efConstruction": 256, + "M": 16, + "num_candidates": 250 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 3846.0365, + "optimize_duration": 1110.4687, + "load_duration": 4956.5052, + "qps": 2353.8935, + "serial_latency_p99": 0.0171, + "serial_latency_p95": 0.0071, + "recall": 0.9056, + "ndcg": 0.9143, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 158.2504, + 791.7118, + 1438.1982, + 2003.6548, + 2210.8624, + 2291.9799, + 2345.97, + 2353.8935 + ], + "conc_latency_p99_list": [ + 0.010709055121405905, + 0.01030786765921221, + 0.01125256240178715, + 0.0189343996412208, + 0.02869696417925298, + 0.04078966366032545, + 0.05675600830181786, + 0.07136866949964316 + ], + "conc_latency_p95_list": [ + 0.0071951633981370815, + 0.00706048959873442, + 0.00878364119926118, + 0.014748687801329647, + 0.022636354199676134, + 0.031156333000035372, + 0.04301601800034405, + 0.05551979320152896 + ], + "conc_latency_avg_list": [ + 0.006316858815334563, + 0.006312276763547482, + 0.006949238477192474, + 0.009956655466073081, + 0.01355885582338353, + 0.017437786081889343, + 0.025496532582614126, + 0.033849936560945634 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "8c60g-force_merge", + "version": "8.17", + "note": "", + "cloud_id": "**********", + "password": "**********" + }, + "db_case_config": { + "element_type": "float", + "index": "hnsw", + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "merge_max_thread_count": 8, + "use_rescore": false, + "oversample_ratio": 2.0, + "use_routing": false, + "use_force_merge": true, + "metric_type": "COSINE", + "efConstruction": 256, + "M": 16, + "num_candidates": 150 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 3846.0365, + "optimize_duration": 1110.4687, + "load_duration": 4956.5052, + "qps": 1623.8421, + "serial_latency_p99": 0.0118, + "serial_latency_p95": 0.0094, + "recall": 0.945, + "ndcg": 0.9479, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 129.0095, + 638.0261, + 1077.0653, + 1477.0665, + 1561.7802, + 1595.9667, + 1623.8421, + 1621.924 + ], + "conc_latency_p99_list": [ + 0.011765603999083404, + 0.012711298419817475, + 0.01651381758929347, + 0.026589661599427918, + 0.039944376738385454, + 0.049404421698636715, + 0.06832758593183824, + 0.08929936919903413 + ], + "conc_latency_p95_list": [ + 0.008827744000882376, + 0.008996219901018777, + 0.012427216298965503, + 0.021186420002777595, + 0.03210561599898938, + 0.04080166250059847, + 0.055962507547701525, + 0.07124235199989926 + ], + "conc_latency_avg_list": [ + 0.00774891642235306, + 0.007833377676777789, + 0.009280148463738688, + 0.01353283532887281, + 0.019196317583287527, + 0.025042119929708374, + 0.0368172028168529, + 0.04911914661560091 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "8c60g-force_merge", + "version": "8.17", + "note": "", + "cloud_id": "**********", + "password": "**********" + }, + "db_case_config": { + "element_type": "float", + "index": "hnsw", + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "merge_max_thread_count": 8, + "use_rescore": false, + "oversample_ratio": 2.0, + "use_routing": false, + "use_force_merge": true, + "metric_type": "COSINE", + "efConstruction": 256, + "M": 16, + "num_candidates": 300 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 3846.0365, + "optimize_duration": 1110.4687, + "load_duration": 4956.5052, + "qps": 2808.2421, + "serial_latency_p99": 0.0095, + "serial_latency_p95": 0.0068, + "recall": 0.8674, + "ndcg": 0.8815, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 155.3245, + 826.7189, + 1600.1053, + 2225.9447, + 2513.1208, + 2695.4952, + 2765.1543, + 2808.2421 + ], + "conc_latency_p99_list": [ + 0.010928568999224795, + 0.010829480089960267, + 0.01071714987985615, + 0.01612071243016544, + 0.02644522499940649, + 0.032847231551932046, + 0.04966854284037254, + 0.06645170240146402 + ], + "conc_latency_p95_list": [ + 0.007179388998338254, + 0.006837483900562801, + 0.007670438798959367, + 0.012552767749366466, + 0.020286900500650518, + 0.02540200199928222, + 0.038052106797840664, + 0.05054814299946883 + ], + "conc_latency_avg_list": [ + 0.0064359595901721245, + 0.006044790967625746, + 0.0062457260062389625, + 0.008978903692601561, + 0.011928161713624801, + 0.01482735261744678, + 0.021619448376489415, + 0.028383759514242164 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "8c60g-force_merge", + "version": "8.17", + "note": "", + "cloud_id": "**********", + "password": "**********" + }, + "db_case_config": { + "element_type": "float", + "index": "hnsw", + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "merge_max_thread_count": 8, + "use_rescore": false, + "oversample_ratio": 2.0, + "use_routing": false, + "use_force_merge": true, + "metric_type": "COSINE", + "efConstruction": 256, + "M": 16, + "num_candidates": 100 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 3846.0365, + "optimize_duration": 1110.4687, + "load_duration": 4956.5052, + "qps": 1482.3772, + "serial_latency_p99": 0.0121, + "serial_latency_p95": 0.0094, + "recall": 0.9523, + "ndcg": 0.9546, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 123.3087, + 580.5824, + 1004.7055, + 1371.9591, + 1426.106, + 1442.8227, + 1482.3772, + 1482.2809 + ], + "conc_latency_p99_list": [ + 0.011828215117784533, + 0.01352971964137398, + 0.0166967718025262, + 0.02956553739913943, + 0.041614612400007904, + 0.05316882036993774, + 0.07466962000107744, + 0.09735826710020776 + ], + "conc_latency_p95_list": [ + 0.009158867201040264, + 0.009766673699778041, + 0.013278135998916696, + 0.023815533299421075, + 0.03488031349843368, + 0.04589065709897113, + 0.06176861299900338, + 0.07706757499909145 + ], + "conc_latency_avg_list": [ + 0.008107228449720227, + 0.008606959040297272, + 0.009948278934310932, + 0.014567306633654263, + 0.02102471552485447, + 0.027699906799067583, + 0.0403594785833045, + 0.05377204408997558 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "8c60g-force_merge", + "version": "8.17", + "note": "", + "cloud_id": "**********", + "password": "**********" + }, + "db_case_config": { + "element_type": "float", + "index": "hnsw", + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "merge_max_thread_count": 8, + "use_rescore": false, + "oversample_ratio": 2.0, + "use_routing": false, + "use_force_merge": true, + "metric_type": "COSINE", + "efConstruction": 256, + "M": 16, + "num_candidates": 350 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 37808.2643, + "optimize_duration": 18845.2628, + "load_duration": 56653.5271, + "qps": 1721.5416, + "serial_latency_p99": 0.0096, + "serial_latency_p95": 0.0084, + "recall": 0.876, + "ndcg": 0.8855, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 18.9602, + 421.7232, + 1110.2953, + 1558.5395, + 1655.8557, + 1687.2055, + 1707.2641, + 1721.5416 + ], + "conc_latency_p99_list": [ + 0.0890053563169205, + 0.06768262584373579, + 0.01639450403279624, + 0.025886146546399667, + 0.03685992147773504, + 0.04851100179657815, + 0.06766616894965408, + 0.08664867073734057 + ], + "conc_latency_p95_list": [ + 0.07462253859848716, + 0.04929285799589706, + 0.011980525049875722, + 0.02050993259763345, + 0.030178915952274107, + 0.039007512998068705, + 0.054956941800628524, + 0.06864605330047198 + ], + "conc_latency_avg_list": [ + 0.0527364081070003, + 0.011852286945072127, + 0.008997451744878971, + 0.012824499661199576, + 0.018105110192998403, + 0.023662544243058387, + 0.03503091284714358, + 0.04628931630721377 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "8c60g-force_merge", + "version": "8.17", + "note": "", + "cloud_id": "**********", + "password": "**********" + }, + "db_case_config": { + "element_type": "float", + "index": "hnsw", + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "merge_max_thread_count": 8, + "use_rescore": false, + "oversample_ratio": 2.0, + "use_routing": false, + "use_force_merge": true, + "metric_type": "COSINE", + "efConstruction": 256, + "M": 16, + "num_candidates": 150 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 37808.2643, + "optimize_duration": 18845.2628, + "load_duration": 56653.5271, + "qps": 1032.9696, + "serial_latency_p99": 0.0148, + "serial_latency_p95": 0.0127, + "recall": 0.9299, + "ndcg": 0.933, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 94.6407, + 456.8911, + 819.2779, + 986.623, + 1016.3699, + 1020.5033, + 1032.9696, + 1017.9882 + ], + "conc_latency_p99_list": [ + 0.01443464591226075, + 0.01668544119311263, + 0.020597075797559213, + 0.04234821415491753, + 0.055556238333374516, + 0.07370169115238243, + 0.10577300176868448, + 0.14174547339440327 + ], + "conc_latency_p95_list": [ + 0.012447767957200992, + 0.01300238400872331, + 0.016574700995988678, + 0.03295493289260776, + 0.04768081354850436, + 0.064796744252817, + 0.09333402040065265, + 0.12088888059952296 + ], + "conc_latency_avg_list": [ + 0.010563127625240488, + 0.010939355078447704, + 0.012200001814344278, + 0.020260497643984427, + 0.02949441625551025, + 0.03917002076625185, + 0.05792509511531056, + 0.07840660381120702 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "8c60g-force_merge", + "version": "8.17", + "note": "", + "cloud_id": "**********", + "password": "**********" + }, + "db_case_config": { + "element_type": "float", + "index": "hnsw", + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "merge_max_thread_count": 8, + "use_rescore": false, + "oversample_ratio": 2.0, + "use_routing": false, + "use_force_merge": true, + "metric_type": "COSINE", + "efConstruction": 256, + "M": 16, + "num_candidates": 350 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 37808.2643, + "optimize_duration": 18845.2628, + "load_duration": 56653.5271, + "qps": 1150.4393, + "serial_latency_p99": 0.0134, + "serial_latency_p95": 0.012, + "recall": 0.9225, + "ndcg": 0.9265, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 104.4067, + 517.3208, + 889.9421, + 1094.8411, + 1095.6488, + 1137.8826, + 1150.4393, + 1141.58 + ], + "conc_latency_p99_list": [ + 0.012909343038918442, + 0.01400524774362566, + 0.019253388400829866, + 0.038147892540728215, + 0.05472438236000016, + 0.06572198009467672, + 0.0952600136399269, + 0.1264052395534234 + ], + "conc_latency_p95_list": [ + 0.011359572803485206, + 0.011475628245534608, + 0.01540375875265454, + 0.03010115729921381, + 0.044985946398810484, + 0.05843431264365791, + 0.08308516180550213, + 0.10663665049651172 + ], + "conc_latency_avg_list": [ + 0.009575407694901247, + 0.009661187708110561, + 0.011232427720710226, + 0.018257383714569215, + 0.027368236626444314, + 0.035086554527816706, + 0.05202559829357753, + 0.06982070456346111 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "8c60g-force_merge", + "version": "8.17", + "note": "", + "cloud_id": "**********", + "password": "**********" + }, + "db_case_config": { + "element_type": "float", + "index": "hnsw", + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "merge_max_thread_count": 8, + "use_rescore": false, + "oversample_ratio": 2.0, + "use_routing": false, + "use_force_merge": true, + "metric_type": "COSINE", + "efConstruction": 256, + "M": 16, + "num_candidates": 300 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 37808.2643, + "optimize_duration": 18845.2628, + "load_duration": 56653.5271, + "qps": 1452.1536, + "serial_latency_p99": 0.0108, + "serial_latency_p95": 0.0092, + "recall": 0.8973, + "ndcg": 0.9042, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 49.0384, + 580.9947, + 986.4739, + 1351.5216, + 1402.1557, + 1419.7304, + 1450.6627, + 1452.1536 + ], + "conc_latency_p99_list": [ + 0.039148551175603646, + 0.013189356601214931, + 0.017704268741654233, + 0.030597097602731108, + 0.04256812395251478, + 0.05555199954222194, + 0.07699622272266429, + 0.0999706184824754 + ], + "conc_latency_p95_list": [ + 0.03537960539688356, + 0.010066490995814093, + 0.013690835254237753, + 0.024310099499416538, + 0.035451141749945236, + 0.04658339719389914, + 0.06417367161193396, + 0.08166611165797803 + ], + "conc_latency_avg_list": [ + 0.020388936871343147, + 0.008602396555117218, + 0.010126462637783804, + 0.014789400451352687, + 0.02138135761146721, + 0.028131951981904997, + 0.04123096160957269, + 0.0548839897278163 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "8c60g-force_merge", + "version": "8.17", + "note": "", + "cloud_id": "**********", + "password": "**********" + }, + "db_case_config": { + "element_type": "float", + "index": "hnsw", + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "merge_max_thread_count": 8, + "use_rescore": false, + "oversample_ratio": 2.0, + "use_routing": false, + "use_force_merge": true, + "metric_type": "COSINE", + "efConstruction": 256, + "M": 16, + "num_candidates": 200 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 37808.2643, + "optimize_duration": 18845.2628, + "load_duration": 56653.5271, + "qps": 2181.3939, + "serial_latency_p99": 0.0094, + "serial_latency_p95": 0.0077, + "recall": 0.8353, + "ndcg": 0.8501, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 3.646, + 29.6904, + 333.5573, + 1900.0888, + 2079.4449, + 2131.3329, + 2181.3939, + 2019.3786 + ], + "conc_latency_p99_list": [ + 0.4185616793336521, + 0.3197556457712197, + 0.22267254341524675, + 0.020585235283360776, + 0.0324391679285327, + 0.04552019087132067, + 0.06043267271961665, + 0.08661333356372784 + ], + "conc_latency_p95_list": [ + 0.38934891536191574, + 0.2844508775517169, + 0.1684353517535783, + 0.01616000900394283, + 0.024561249790713186, + 0.0332476719981059, + 0.04531921409798087, + 0.06508466459927148 + ], + "conc_latency_avg_list": [ + 0.2742520264012538, + 0.1683271812554273, + 0.029971614569954105, + 0.010518879796956216, + 0.014417846781857802, + 0.018753533944310726, + 0.02740638227517153, + 0.039480719052758934 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "8c60g-force_merge", + "version": "8.17", + "note": "", + "cloud_id": "**********", + "password": "**********" + }, + "db_case_config": { + "element_type": "float", + "index": "hnsw", + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "merge_max_thread_count": 8, + "use_rescore": false, + "oversample_ratio": 2.0, + "use_routing": false, + "use_force_merge": true, + "metric_type": "COSINE", + "efConstruction": 256, + "M": 16, + "num_candidates": 100 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 37808.2643, + "optimize_duration": 18845.2628, + "load_duration": 56653.5271, + "qps": 1295.5543, + "serial_latency_p99": 0.0112, + "serial_latency_p95": 0.0102, + "recall": 0.9126, + "ndcg": 0.9176, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 108.6988, + 554.0053, + 962.0715, + 1222.1509, + 1262.7418, + 1287.8623, + 1295.5543, + 1295.4142 + ], + "conc_latency_p99_list": [ + 0.012900208660430504, + 0.013385620156768804, + 0.017747372422018085, + 0.033394850781187424, + 0.04573169803712508, + 0.05876307546350308, + 0.08581843032181498, + 0.1129824651160743 + ], + "conc_latency_p95_list": [ + 0.010918107806355692, + 0.01064183129929006, + 0.014414189194212666, + 0.02686949290437041, + 0.03948758620244917, + 0.051792045756883454, + 0.0734679256027448, + 0.09282746819080781 + ], + "conc_latency_avg_list": [ + 0.009197126244932163, + 0.00902096815373196, + 0.010389166613132592, + 0.016355007809079263, + 0.02374101070396333, + 0.03104020946657135, + 0.046198905251943534, + 0.06155043230356708 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "8c60g-force_merge", + "version": "8.17", + "note": "", + "cloud_id": "**********", + "password": "**********" + }, + "db_case_config": { + "element_type": "float", + "index": "hnsw", + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "merge_max_thread_count": 8, + "use_rescore": false, + "oversample_ratio": 2.0, + "use_routing": false, + "use_force_merge": true, + "metric_type": "COSINE", + "efConstruction": 256, + "M": 16, + "num_candidates": 250 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + } + ], + "file_fmt": "result_{}_{}_{}.json", + "timestamp": 1770595200.0 +} \ No newline at end of file diff --git a/vectordb_bench/results/FullTextSearch/ElasticCloud/result_20260626_fts_standard_elasticcloud.json b/vectordb_bench/results/FullTextSearch/ElasticCloud/result_20260626_fts_standard_elasticcloud.json new file mode 100644 index 000000000..1ac7e9773 --- /dev/null +++ b/vectordb_bench/results/FullTextSearch/ElasticCloud/result_20260626_fts_standard_elasticcloud.json @@ -0,0 +1,2326 @@ +{ + "run_id": "fts_standard_elasticcloud", + "task_label": "fts_standard", + "results": [ + { + "metrics": { + "max_load_count": 0, + "insert_duration": 92.7146, + "optimize_duration": 90.3815, + "load_duration": 183.0961, + "qps": 1353.3032, + "serial_latency_p99": 0.0303, + "serial_latency_p95": 0.0232, + "recall": 0.7637, + "ndcg": 0.6244, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1344.6128, + 1353.3032 + ], + "conc_latency_p99_list": [ + 0.08339079822006171, + 0.09601655204241978 + ], + "conc_latency_p95_list": [ + 0.06611652700194098, + 0.08258865799944035 + ], + "conc_latency_avg_list": [ + 0.04456428650467425, + 0.05904593880422133 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 5233329, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 0, + "fts_manifest": { + "bm25": { + "k1": 1.2, + "b": 0.75, + "avgdl": 47.43239876568051 + }, + "analyzer": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "applied_bm25_params": { + "k1": 1.2, + "b": 0.75 + }, + "unapplied_bm25_params": { + "avgdl": 47.43239876568051 + }, + "applied_analyzer_params": {}, + "unapplied_analyzer_params": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [], + "mrr": 0.755 + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-06-23T17:49:51.080771", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": 1.2, + "bm25_b": 0.75 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 17.887, + "optimize_duration": 35.1281, + "load_duration": 53.015, + "qps": 4489.7666, + "serial_latency_p99": 0.0086, + "serial_latency_p95": 0.0067, + "recall": 0.8378, + "ndcg": 0.7287, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 4481.0331, + 4489.7666 + ], + "conc_latency_p99_list": [ + 0.025020206998306094, + 0.029247877919842704 + ], + "conc_latency_p95_list": [ + 0.02024304099904839, + 0.02491419739963021 + ], + "conc_latency_avg_list": [ + 0.013372686260277478, + 0.01778988601007296 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 1000000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 0, + "fts_manifest": { + "bm25": { + "k1": 1.2, + "b": 0.75, + "avgdl": 55.803761 + }, + "analyzer": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "applied_bm25_params": { + "k1": 1.2, + "b": 0.75 + }, + "unapplied_bm25_params": { + "avgdl": 55.803761 + }, + "applied_analyzer_params": {}, + "unapplied_analyzer_params": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [], + "mrr": 0.8598 + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-06-23T17:43:25.431349", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": 1.2, + "bm25_b": 0.75 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Medium (1M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1.9662, + "optimize_duration": 31.09, + "load_duration": 33.0562, + "qps": 12590.5736, + "serial_latency_p99": 0.0023, + "serial_latency_p95": 0.002, + "recall": 0.9203, + "ndcg": 0.842, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 12534.1131, + 12590.5736 + ], + "conc_latency_p99_list": [ + 0.008597700440295735, + 0.010427862039068704 + ], + "conc_latency_p95_list": [ + 0.007032485200761586, + 0.008721868197972072 + ], + "conc_latency_avg_list": [ + 0.0047639834292315195, + 0.006304263635102981 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 100000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 0, + "fts_manifest": { + "bm25": { + "k1": 1.2, + "b": 0.75, + "avgdl": 56.88413 + }, + "analyzer": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "applied_bm25_params": { + "k1": 1.2, + "b": 0.75 + }, + "unapplied_bm25_params": { + "avgdl": 56.88413 + }, + "applied_analyzer_params": {}, + "unapplied_analyzer_params": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [], + "mrr": 0.9446 + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-06-23T17:38:23.654768", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": 1.2, + "bm25_b": 0.75 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Small (100K documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 160.1365, + "optimize_duration": 92.5894, + "load_duration": 252.7259, + "qps": 2686.9741, + "serial_latency_p99": 0.0222, + "serial_latency_p95": 0.0157, + "recall": 0.6228, + "ndcg": 0.2732, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 2686.9741, + 2663.4012 + ], + "conc_latency_p99_list": [ + 0.05383890143901231, + 0.06195318944053717 + ], + "conc_latency_p95_list": [ + 0.039473479198204584, + 0.04770255699986588 + ], + "conc_latency_avg_list": [ + 0.02230479944928826, + 0.029977797620890846 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 8841823, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 0, + "fts_manifest": { + "bm25": { + "k1": 1.2, + "b": 0.75, + "avgdl": 57.83363736188793 + }, + "analyzer": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "applied_bm25_params": { + "k1": 1.2, + "b": 0.75 + }, + "unapplied_bm25_params": { + "avgdl": 57.83363736188793 + }, + "applied_analyzer_params": {}, + "unapplied_analyzer_params": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [], + "mrr": 0.1862 + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-06-23T17:25:04.982818", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": 1.2, + "bm25_b": 0.75 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 17.8893, + "optimize_duration": 34.2775, + "load_duration": 52.1668, + "qps": 10152.153, + "serial_latency_p99": 0.0043, + "serial_latency_p95": 0.0034, + "recall": 0.8028, + "ndcg": 0.5222, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 10134.0539, + 10152.153 + ], + "conc_latency_p99_list": [ + 0.014198277799223388, + 0.016789430999779142 + ], + "conc_latency_p95_list": [ + 0.01101246209909732, + 0.013366064998990623 + ], + "conc_latency_avg_list": [ + 0.005904385368471612, + 0.007849020531480228 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 1000000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 0, + "fts_manifest": { + "bm25": { + "k1": 1.2, + "b": 0.75, + "avgdl": 57.242324 + }, + "analyzer": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "applied_bm25_params": { + "k1": 1.2, + "b": 0.75 + }, + "unapplied_bm25_params": { + "avgdl": 57.242324 + }, + "applied_analyzer_params": {}, + "unapplied_analyzer_params": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [], + "mrr": 0.4526 + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-06-23T17:19:23.319688", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": 1.2, + "bm25_b": 0.75 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Medium (1M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1.8862, + "optimize_duration": 31.0352, + "load_duration": 32.9214, + "qps": 12530.9805, + "serial_latency_p99": 0.0016, + "serial_latency_p95": 0.0014, + "recall": 0.9116, + "ndcg": 0.7158, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 12508.8898, + 12530.9805 + ], + "conc_latency_p99_list": [ + 0.00861286495841341, + 0.00828596436098451 + ], + "conc_latency_p95_list": [ + 0.005912762001389639, + 0.007557337400794491 + ], + "conc_latency_avg_list": [ + 0.004776813722617175, + 0.006322142056532246 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 100000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 0, + "fts_manifest": { + "bm25": { + "k1": 1.2, + "b": 0.75, + "avgdl": 55.01342 + }, + "analyzer": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "applied_bm25_params": { + "k1": 1.2, + "b": 0.75 + }, + "unapplied_bm25_params": { + "avgdl": 55.01342 + }, + "applied_analyzer_params": {}, + "unapplied_analyzer_params": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [], + "mrr": 0.6665 + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-06-23T17:14:30.502248", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": 1.2, + "bm25_b": 0.75 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Small (100K documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 210.0847, + "optimize_duration": 93.2572, + "load_duration": 303.3419, + "qps": 721.3662, + "serial_latency_p99": 0.07, + "serial_latency_p95": 0.0475, + "recall": 0.7966, + "ndcg": 0.6175, + "mrr": 0.6258, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 709.952, + 721.3662 + ], + "conc_latency_p99_list": [ + 0.1806982008819614, + 0.2100805627823138 + ], + "conc_latency_p95_list": [ + 0.13791442114634248, + 0.16302671675002783 + ], + "conc_latency_avg_list": [ + 0.08436115583290123, + 0.11065719012112213 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 5233329, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 2616664, + "filter_rate": 0.5, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 2616665, + "matched_doc_ratio": 0.5, + "original_query_count": 7405, + "filtered_query_count": 5547, + "filtered_query_ratio": 0.749088, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 6846 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 5547, + "full_query_count": 7405 + }, + "num_per_batch": 1000, + "load_concurrency": 4, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "6d07ef1559b6411b9d0afa54df3785aa", + "source_task_label": "fts_filtered_serial_elasticsearch_hotpotqa-large_r50_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_elasticsearch_hotpotqa-large_r50_permuted_elasticcloud.json", + "source_sha256": "91b664746a916c20e5a0af5fde40bfcc30ccc8125a2dfaece86511f68ea94583", + "source_db_label": "2026-08-03T16:47:58.795789", + "source_stages": [ + "drop_old", + "load", + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-07-16T19:33:09.977867", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.5 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "drop_old", + "load", + "search_concurrent", + "search_serial" + ], + "load_concurrency": 4 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 750.5232, + "serial_latency_p99": 0.0637, + "serial_latency_p95": 0.0436, + "recall": 0.8263, + "ndcg": 0.6439, + "mrr": 0.6188, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 742.2813, + 750.5232 + ], + "conc_latency_p99_list": [ + 0.17308506104236582, + 0.19856040821949153 + ], + "conc_latency_p95_list": [ + 0.12934453199850396, + 0.1560941719049879 + ], + "conc_latency_avg_list": [ + 0.08063411026564973, + 0.10631068993652631 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 3924996, + "filter_rate": 0.75, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 1308333, + "matched_doc_ratio": 0.25, + "original_query_count": 7405, + "filtered_query_count": 3175, + "filtered_query_ratio": 0.428764, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 3379 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 3175, + "full_query_count": 7405 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "17bc3bc82c7a4367b901f21e8415867a", + "source_task_label": "fts_filtered_serial_elasticsearch_hotpotqa-large_r75_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_elasticsearch_hotpotqa-large_r75_permuted_elasticcloud.json", + "source_sha256": "0ed262bb98569e2c29ffda21c498767d25ff895a23fe02a78db6b40b2f0191cc", + "source_db_label": "2026-08-03T16:56:16.769773", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-07-16T19:41:37.525743", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.75 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 770.9499, + "serial_latency_p99": 0.0724, + "serial_latency_p95": 0.0508, + "recall": 0.8665, + "ndcg": 0.7004, + "mrr": 0.6627, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 759.173, + 770.9499 + ], + "conc_latency_p99_list": [ + 0.15933862284160563, + 0.18434771904256195 + ], + "conc_latency_p95_list": [ + 0.12385570425285548, + 0.14966088689725432 + ], + "conc_latency_avg_list": [ + 0.07888747691351794, + 0.10353734658255655 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 4709996, + "filter_rate": 0.9, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 523333, + "matched_doc_ratio": 0.1, + "original_query_count": 7405, + "filtered_query_count": 1367, + "filtered_query_ratio": 0.184605, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 1335 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 1367, + "full_query_count": 7405 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "b4641cb14b1644279c5f35fee2458dfb", + "source_task_label": "fts_filtered_serial_elasticsearch_hotpotqa-large_r90_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_elasticsearch_hotpotqa-large_r90_permuted_elasticcloud.json", + "source_sha256": "9a52e707376a127c8d538bab1a67883b6c5521df0d866d1a72aaab1cca0ddf2c", + "source_db_label": "2026-08-03T16:59:14.726191", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-07-16T19:45:01.768660", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.9 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 774.5308, + "serial_latency_p99": 0.0542, + "serial_latency_p95": 0.0383, + "recall": 0.884, + "ndcg": 0.7266, + "mrr": 0.6865, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 774.5308, + 772.2879 + ], + "conc_latency_p99_list": [ + 0.15306021131982533, + 0.17734859948323 + ], + "conc_latency_p95_list": [ + 0.12027961940184463, + 0.14829936109745176 + ], + "conc_latency_avg_list": [ + 0.07731039411178581, + 0.10336622360207741 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 4971662, + "filter_rate": 0.95, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 261667, + "matched_doc_ratio": 0.05, + "original_query_count": 7405, + "filtered_query_count": 698, + "filtered_query_ratio": 0.094261, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 664 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 698, + "full_query_count": 7405 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "a72a9198404847c696aa094dc2ecebce", + "source_task_label": "fts_filtered_serial_elasticsearch_hotpotqa-large_r95_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_elasticsearch_hotpotqa-large_r95_permuted_elasticcloud.json", + "source_sha256": "cd51a79edd64d94df8814bbfb745ddb555e251c338a2ddafae49c6cf989e5ea2", + "source_db_label": "2026-08-03T17:01:44.955740", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-07-16T19:48:27.444308", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.95 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 936.4393, + "serial_latency_p99": 0.0359, + "serial_latency_p95": 0.0298, + "recall": 0.898, + "ndcg": 0.7825, + "mrr": 0.7494, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 919.3657, + 936.4393 + ], + "conc_latency_p99_list": [ + 0.11650546061813662, + 0.13283855842935735 + ], + "conc_latency_p95_list": [ + 0.09433838454860961, + 0.11730172135248722 + ], + "conc_latency_avg_list": [ + 0.06513757521688086, + 0.08528450502047526 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 5180995, + "filter_rate": 0.99, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 52334, + "matched_doc_ratio": 0.01, + "original_query_count": 7405, + "filtered_query_count": 147, + "filtered_query_ratio": 0.019851, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 136 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 147, + "full_query_count": 7405 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "482bc4f2cf3a4c1692ff65756cd33ea5", + "source_task_label": "fts_filtered_serial_elasticsearch_hotpotqa-large_r99_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_elasticsearch_hotpotqa-large_r99_permuted_elasticcloud.json", + "source_sha256": "2f644813b64d937af334d5c52d120a4d89a096927034adb6f11c7cfd63bb95d5", + "source_db_label": "2026-08-03T17:03:52.164545", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-07-16T19:51:49.557699", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.99 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 305.6984, + "optimize_duration": 93.6347, + "load_duration": 399.3331, + "qps": 1670.2698, + "serial_latency_p99": 0.0427, + "serial_latency_p95": 0.0268, + "recall": 0.6974, + "ndcg": 0.3455, + "mrr": 0.256, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1613.9683, + 1670.2698 + ], + "conc_latency_p99_list": [ + 0.10500255563529215, + 0.10993121209095964 + ], + "conc_latency_p95_list": [ + 0.068664093100233, + 0.07755282529615215 + ], + "conc_latency_avg_list": [ + 0.03712288018409116, + 0.04780773417065931 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 8841823, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 4420911, + "filter_rate": 0.5, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 4420912, + "matched_doc_ratio": 0.5, + "original_query_count": 6980, + "filtered_query_count": 3658, + "filtered_query_ratio": 0.524069, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 3758 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 3658, + "full_query_count": 6980 + }, + "num_per_batch": 1000, + "load_concurrency": 4, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "44d302a866ef41f487cd50bab4cd6ee1", + "source_task_label": "fts_filtered_serial_elasticsearch_msmarco-large_r50_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_elasticsearch_msmarco-large_r50_permuted_elasticcloud.json", + "source_sha256": "0573762c9a2d93cd37248a6e6c63d2a0f6e245844e1f18ef83fa4368bea1ee57", + "source_db_label": "2026-08-03T16:36:18.370955", + "source_stages": [ + "drop_old", + "load", + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-07-16T18:34:54.790049", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.5 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "drop_old", + "load", + "search_concurrent", + "search_serial" + ], + "load_concurrency": 4 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 1760.9692, + "serial_latency_p99": 0.0375, + "serial_latency_p95": 0.0237, + "recall": 0.7618, + "ndcg": 0.4234, + "mrr": 0.3358, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1760.9692, + 1748.2651 + ], + "conc_latency_p99_list": [ + 0.08974919100000989, + 0.10211793256872619 + ], + "conc_latency_p95_list": [ + 0.060971413004153874, + 0.0733459422524902 + ], + "conc_latency_avg_list": [ + 0.0339761071088688, + 0.04568243171278137 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 6631367, + "filter_rate": 0.75, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 2210456, + "matched_doc_ratio": 0.25, + "original_query_count": 6980, + "filtered_query_count": 1818, + "filtered_query_ratio": 0.260458, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 1839 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 1818, + "full_query_count": 6980 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "e798f27d27574fb4a070b632d6a39fd3", + "source_task_label": "fts_filtered_serial_elasticsearch_msmarco-large_r75_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_elasticsearch_msmarco-large_r75_permuted_elasticcloud.json", + "source_sha256": "2633cc2c75fdae118a8be7dd7cde4faa8ee705a35a1d6812d2355784c395c46a", + "source_db_label": "2026-08-03T16:44:18.348174", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-07-16T18:43:49.325484", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.75 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 1727.6187, + "serial_latency_p99": 0.0349, + "serial_latency_p95": 0.0232, + "recall": 0.8383, + "ndcg": 0.5152, + "mrr": 0.4301, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1727.6187, + 1712.5496 + ], + "conc_latency_p99_list": [ + 0.08606930284106058, + 0.09963348716031763 + ], + "conc_latency_p95_list": [ + 0.06112156380477239, + 0.07310520800238009 + ], + "conc_latency_avg_list": [ + 0.03468250891671445, + 0.04662555647302419 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 7957640, + "filter_rate": 0.9, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 884183, + "matched_doc_ratio": 0.1, + "original_query_count": 6980, + "filtered_query_count": 739, + "filtered_query_ratio": 0.105874, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 740 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 739, + "full_query_count": 6980 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "3db387eca78a41b988db0fa5d8653078", + "source_task_label": "fts_filtered_serial_elasticsearch_msmarco-large_r90_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_elasticsearch_msmarco-large_r90_permuted_elasticcloud.json", + "source_sha256": "323dda4c7f6f14141ee1ff1b1df6b7d6dc20269bc1d01f90f36d22c143780f49", + "source_db_label": "2026-08-03T16:45:23.353032", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-07-16T18:46:08.382474", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.9 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 1640.447, + "serial_latency_p99": 0.0328, + "serial_latency_p95": 0.022, + "recall": 0.8559, + "ndcg": 0.5826, + "mrr": 0.5105, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1637.2394, + 1640.447 + ], + "conc_latency_p99_list": [ + 0.08551632568014624, + 0.09862579727101546 + ], + "conc_latency_p95_list": [ + 0.06308142885354755, + 0.07469368650054091 + ], + "conc_latency_avg_list": [ + 0.036597530028333033, + 0.048665543477514936 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 8399731, + "filter_rate": 0.95, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 442092, + "matched_doc_ratio": 0.05, + "original_query_count": 6980, + "filtered_query_count": 347, + "filtered_query_ratio": 0.049713, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 347 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 347, + "full_query_count": 6980 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "8a211aced09a403486d088c32fd068e2", + "source_task_label": "fts_filtered_serial_elasticsearch_msmarco-large_r95_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_elasticsearch_msmarco-large_r95_permuted_elasticcloud.json", + "source_sha256": "c6d3dc57545fd1e5b9c44ae31f26e985c80ad3eebddd838c559228c52338170c", + "source_db_label": "2026-08-03T16:46:18.769194", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-07-16T18:48:23.449047", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.95 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 1760.9558, + "serial_latency_p99": 0.0221, + "serial_latency_p95": 0.0191, + "recall": 0.9403, + "ndcg": 0.7033, + "mrr": 0.6373, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1754.6975, + 1760.9558 + ], + "conc_latency_p99_list": [ + 0.06734019880532284, + 0.07769490615988613 + ], + "conc_latency_p95_list": [ + 0.052860249001241755, + 0.06493684799788751 + ], + "conc_latency_avg_list": [ + 0.03414497660724748, + 0.04535883119607527 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 8753404, + "filter_rate": 0.99, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 88419, + "matched_doc_ratio": 0.01, + "original_query_count": 6980, + "filtered_query_count": 67, + "filtered_query_ratio": 0.009599, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 67 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 67, + "full_query_count": 6980 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "c459c22a67544caaa467bacec9626531", + "source_task_label": "fts_filtered_serial_elasticsearch_msmarco-large_r99_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_elasticsearch_msmarco-large_r99_permuted_elasticcloud.json", + "source_sha256": "39cc6a5202b5daa079b35cd28d86e379866dced531bc59f50a2ac3a2ba30f78a", + "source_db_label": "2026-08-03T16:47:10.714140", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ElasticCloud", + "db_config": { + "db_label": "2026-07-16T18:50:39.525006", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"elasticsearch/v1\",\n \"backend_version\": \"9.4.3\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"elasticsearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "cloud_id": null, + "scheme": "http", + "host": "**********", + "port": 9200, + "user": "elastic", + "user_name": null, + "password": "**********", + "use_ssl": false, + "verify_certs": true + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "use_force_merge": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.99 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + } + ], + "file_fmt": "result_{}_{}_{}.json", + "timestamp": 1782172800.0 +} diff --git a/vectordb_bench/results/FullTextSearch/OpenSearch/result_20260708_fts_standard_opensearch.json b/vectordb_bench/results/FullTextSearch/OpenSearch/result_20260708_fts_standard_opensearch.json new file mode 100644 index 000000000..d30aa6227 --- /dev/null +++ b/vectordb_bench/results/FullTextSearch/OpenSearch/result_20260708_fts_standard_opensearch.json @@ -0,0 +1,2100 @@ +{ + "run_id": "fb9d4d97c0fd414f8b462556670c3105", + "task_label": "opensearch37_semantic_ids_only", + "results": [ + { + "metrics": { + "max_load_count": 0, + "insert_duration": 21.4087, + "optimize_duration": 30.5468, + "load_duration": 51.9554, + "qps": 12691.5259, + "serial_latency_p99": 0.0016, + "serial_latency_p95": 0.0014, + "recall": 0.9115, + "ndcg": 0.7158, + "mrr": 0.6664, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 12490.6378, + 12691.5259 + ], + "conc_latency_p99_list": [ + 0.009508526180288754, + 0.008180375498341164 + ], + "conc_latency_p95_list": [ + 0.006050063249676896, + 0.007465197501005605 + ], + "conc_latency_avg_list": [ + 0.004786182434018657, + 0.006250595112561167 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 100000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 0 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_os37_msmarco_small", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Small (100K documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 36.5969, + "optimize_duration": 30.0643, + "load_duration": 66.6612, + "qps": 9716.9805, + "serial_latency_p99": 0.0045, + "serial_latency_p95": 0.0035, + "recall": 0.8028, + "ndcg": 0.5222, + "mrr": 0.4526, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 9679.3154, + 9716.9805 + ], + "conc_latency_p99_list": [ + 0.014330312998936279, + 0.016602437859110065 + ], + "conc_latency_p95_list": [ + 0.01117035200149985, + 0.01337789999961386 + ], + "conc_latency_avg_list": [ + 0.0061837669065991155, + 0.008203579954480796 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 1000000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 0 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_os37_msmarco_medium", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Medium (1M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 241.1154, + "optimize_duration": 64.3404, + "load_duration": 305.4557, + "qps": 2765.6385, + "serial_latency_p99": 0.0232, + "serial_latency_p95": 0.0159, + "recall": 0.6228, + "ndcg": 0.2731, + "mrr": 0.186, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 2765.6385, + 2703.4414 + ], + "conc_latency_p99_list": [ + 0.054147378889720287, + 0.06431732813121925 + ], + "conc_latency_p95_list": [ + 0.03906455834985536, + 0.04768981275119586 + ], + "conc_latency_avg_list": [ + 0.021667828285827452, + 0.029549008838550658 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 8841823, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 0 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_os37_msmarco_large", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 15.3955, + "optimize_duration": 30.623, + "load_duration": 46.0185, + "qps": 12605.0846, + "serial_latency_p99": 0.0024, + "serial_latency_p95": 0.0021, + "recall": 0.9203, + "ndcg": 0.842, + "mrr": 0.9446, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 12191.1982, + 12605.0846 + ], + "conc_latency_p99_list": [ + 0.010707631260593202, + 0.013225803580644424 + ], + "conc_latency_p95_list": [ + 0.00833549370181572, + 0.010521701102152285 + ], + "conc_latency_avg_list": [ + 0.004901968566726041, + 0.0063033020004832005 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 100000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 0 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_os37_hotpotqa_small", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Small (100K documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 33.1365, + "optimize_duration": 30.857, + "load_duration": 63.9935, + "qps": 4279.073, + "serial_latency_p99": 0.009, + "serial_latency_p95": 0.0071, + "recall": 0.8378, + "ndcg": 0.7287, + "mrr": 0.8598, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 4279.073, + 4277.3037 + ], + "conc_latency_p99_list": [ + 0.025680999840842586, + 0.03080206583093969 + ], + "conc_latency_p95_list": [ + 0.02089815480067045, + 0.02596823259973461 + ], + "conc_latency_avg_list": [ + 0.01400255309733968, + 0.018675460777613128 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 1000000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 0 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_os37_hotpotqa_medium", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Medium (1M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 132.2746, + "optimize_duration": 61.683, + "load_duration": 193.9576, + "qps": 1336.4625, + "serial_latency_p99": 0.0316, + "serial_latency_p95": 0.0241, + "recall": 0.7637, + "ndcg": 0.6243, + "mrr": 0.7549, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1335.4086, + 1336.4625 + ], + "conc_latency_p99_list": [ + 0.08600163375929698, + 0.10016711670032244 + ], + "conc_latency_p95_list": [ + 0.06740941459793247, + 0.08448904250144551 + ], + "conc_latency_avg_list": [ + 0.044878777739965396, + 0.0597764434350997 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 5233329, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 0 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_os37_hotpotqa_large", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 150.1223, + "optimize_duration": 34.1788, + "load_duration": 184.3012, + "qps": 667.304, + "serial_latency_p99": 0.0803, + "serial_latency_p95": 0.0553, + "recall": 0.7965, + "ndcg": 0.6174, + "mrr": 0.6257, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 667.304, + 666.4777 + ], + "conc_latency_p99_list": [ + 0.1921520423081529, + 0.22862113459021222 + ], + "conc_latency_p95_list": [ + 0.14732917190267472, + 0.17784609899790668 + ], + "conc_latency_avg_list": [ + 0.08955421200884714, + 0.11974453346600444 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 5233329, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 2616664, + "filter_rate": 0.5, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 2616665, + "matched_doc_ratio": 0.5, + "original_query_count": 7405, + "filtered_query_count": 5547, + "filtered_query_ratio": 0.749088, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 6846 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 5547, + "full_query_count": 7405 + }, + "num_per_batch": 1000, + "load_concurrency": 4, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "75d27b07a19d488e9dd9d111eb2fd30e", + "source_task_label": "fts_filtered_serial_opensearch_hotpotqa-large_r50_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_opensearch_hotpotqa-large_r50_permuted_ossopensearch.json", + "source_sha256": "81f6b480b6063c23cc4b61d58b7471b8170fa6bbe3e267753bdff3d6cc6e55cf", + "source_db_label": "", + "source_stages": [ + "drop_old", + "load", + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_total_series", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.5 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "drop_old", + "load", + "search_concurrent", + "search_serial" + ], + "load_concurrency": 4 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 731.8949, + "serial_latency_p99": 0.0724, + "serial_latency_p95": 0.0511, + "recall": 0.8263, + "ndcg": 0.6439, + "mrr": 0.6187, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 720.3874, + 731.8949 + ], + "conc_latency_p99_list": [ + 0.1764921818590665, + 0.20388233807985673 + ], + "conc_latency_p95_list": [ + 0.1350544641034503, + 0.16113367400394052 + ], + "conc_latency_avg_list": [ + 0.08302399005114736, + 0.10901853523287403 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 3924996, + "filter_rate": 0.75, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 1308333, + "matched_doc_ratio": 0.25, + "original_query_count": 7405, + "filtered_query_count": 3175, + "filtered_query_ratio": 0.428764, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 3379 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 3175, + "full_query_count": 7405 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "eb07a1bea78a41778047b3f591b54b6a", + "source_task_label": "fts_filtered_serial_opensearch_hotpotqa-large_r75_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_opensearch_hotpotqa-large_r75_permuted_ossopensearch.json", + "source_sha256": "fe28e8d8cc38f6bc7da35157b17f1419f3ae1bde6cbde25f3b734ff829e0d15a", + "source_db_label": "", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_total_series", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.75 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 774.4857, + "serial_latency_p99": 0.063, + "serial_latency_p95": 0.0455, + "recall": 0.8665, + "ndcg": 0.7004, + "mrr": 0.6627, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 774.0823, + 774.4857 + ], + "conc_latency_p99_list": [ + 0.15563926174960205, + 0.18285104553018763 + ], + "conc_latency_p95_list": [ + 0.12185666524928812, + 0.15047706774530525 + ], + "conc_latency_avg_list": [ + 0.07737237257563531, + 0.10304668327704196 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 4709996, + "filter_rate": 0.9, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 523333, + "matched_doc_ratio": 0.1, + "original_query_count": 7405, + "filtered_query_count": 1367, + "filtered_query_ratio": 0.184605, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 1335 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 1367, + "full_query_count": 7405 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "bbd65a96f4b94f63859e386d8b479262", + "source_task_label": "fts_filtered_serial_opensearch_hotpotqa-large_r90_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_opensearch_hotpotqa-large_r90_permuted_ossopensearch.json", + "source_sha256": "5e4b7a9ec8eea54738159a4c1ea8b7e4e5339420e9a30146dc3921ced18484fd", + "source_db_label": "", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_total_series", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.9 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 822.775, + "serial_latency_p99": 0.0559, + "serial_latency_p95": 0.0403, + "recall": 0.884, + "ndcg": 0.7266, + "mrr": 0.6865, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 822.775, + 822.129 + ], + "conc_latency_p99_list": [ + 0.1412675818782008, + 0.1656299286980356 + ], + "conc_latency_p95_list": [ + 0.11175147520334575, + 0.13831253509706584 + ], + "conc_latency_avg_list": [ + 0.07280328828906549, + 0.09711557214002686 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 4971662, + "filter_rate": 0.95, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 261667, + "matched_doc_ratio": 0.05, + "original_query_count": 7405, + "filtered_query_count": 698, + "filtered_query_ratio": 0.094261, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 664 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 698, + "full_query_count": 7405 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "cafbf08c0a1b4340aeffdd5c17a98e8a", + "source_task_label": "fts_filtered_serial_opensearch_hotpotqa-large_r95_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_opensearch_hotpotqa-large_r95_permuted_ossopensearch.json", + "source_sha256": "d347d5d0d87058dc524a0f64457fc34dbd52fe7eab60fd50f1e381a61aa8e723", + "source_db_label": "", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_total_series", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.95 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 1001.7318, + "serial_latency_p99": 0.0382, + "serial_latency_p95": 0.0313, + "recall": 0.898, + "ndcg": 0.7825, + "mrr": 0.7494, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1001.074, + 1001.7318 + ], + "conc_latency_p99_list": [ + 0.10869441425413238, + 0.1255568589524046 + ], + "conc_latency_p95_list": [ + 0.08671272930077974, + 0.10970903755223843 + ], + "conc_latency_avg_list": [ + 0.05986702706436918, + 0.07976030199309388 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 5180995, + "filter_rate": 0.99, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 52334, + "matched_doc_ratio": 0.01, + "original_query_count": 7405, + "filtered_query_count": 147, + "filtered_query_ratio": 0.019851, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 136 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 147, + "full_query_count": 7405 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "f540cef0cce545a2b37e3666c93c2ac0", + "source_task_label": "fts_filtered_serial_opensearch_hotpotqa-large_r99_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_opensearch_hotpotqa-large_r99_permuted_ossopensearch.json", + "source_sha256": "c40d03752bfaa9281670ea8acbb2ff9525f9a093e8fe92246ccc89ff1fc7ad33", + "source_db_label": "", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_total_series", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.99 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 235.9506, + "optimize_duration": 63.7682, + "load_duration": 299.7188, + "qps": 1531.2932, + "serial_latency_p99": 0.0625, + "serial_latency_p95": 0.04, + "recall": 0.6974, + "ndcg": 0.3452, + "mrr": 0.2556, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1397.028, + 1531.2932 + ], + "conc_latency_p99_list": [ + 0.12297188279801045, + 0.11898466303653542 + ], + "conc_latency_p95_list": [ + 0.07994781309971585, + 0.08496303739520955 + ], + "conc_latency_avg_list": [ + 0.042852920943267835, + 0.05207596939025791 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 8841823, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 4420911, + "filter_rate": 0.5, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 4420912, + "matched_doc_ratio": 0.5, + "original_query_count": 6980, + "filtered_query_count": 3658, + "filtered_query_ratio": 0.524069, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 3758 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 3658, + "full_query_count": 6980 + }, + "num_per_batch": 1000, + "load_concurrency": 4, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "d137f84a931c420387dcf03ee9a1fac1", + "source_task_label": "fts_filtered_serial_opensearch_msmarco-large_r50_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_opensearch_msmarco-large_r50_permuted_ossopensearch.json", + "source_sha256": "03db427c5251728b1b5a515d2c6428d1f69ad8f7511ab3dad76ce9a693fad889", + "source_db_label": "", + "source_stages": [ + "drop_old", + "load", + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_total_series", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.5 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "drop_old", + "load", + "search_concurrent", + "search_serial" + ], + "load_concurrency": 4 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 1606.6222, + "serial_latency_p99": 0.0418, + "serial_latency_p95": 0.0275, + "recall": 0.7618, + "ndcg": 0.423, + "mrr": 0.3352, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1606.6222, + 1590.4299 + ], + "conc_latency_p99_list": [ + 0.09791465523405339, + 0.11049418440234161 + ], + "conc_latency_p95_list": [ + 0.06849266580247786, + 0.08139459499943769 + ], + "conc_latency_avg_list": [ + 0.03727788527657192, + 0.050209997870867544 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 6631367, + "filter_rate": 0.75, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 2210456, + "matched_doc_ratio": 0.25, + "original_query_count": 6980, + "filtered_query_count": 1818, + "filtered_query_ratio": 0.260458, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 1839 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 1818, + "full_query_count": 6980 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "5a5f2ed5c49e4f4996ba8aa5569549cd", + "source_task_label": "fts_filtered_serial_opensearch_msmarco-large_r75_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_opensearch_msmarco-large_r75_permuted_ossopensearch.json", + "source_sha256": "ef2e679c0369c5c6d3d00b762073bbc522c148b997c149bd887b5cc634bfc648", + "source_db_label": "", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_total_series", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.75 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 1695.751, + "serial_latency_p99": 0.036, + "serial_latency_p95": 0.0245, + "recall": 0.8383, + "ndcg": 0.5152, + "mrr": 0.43, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1640.8756, + 1695.751 + ], + "conc_latency_p99_list": [ + 0.09178599175880653, + 0.1001698837034927 + ], + "conc_latency_p95_list": [ + 0.06526272699993568, + 0.07415721294491956 + ], + "conc_latency_avg_list": [ + 0.0365111554621457, + 0.047110976089449794 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 7957640, + "filter_rate": 0.9, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 884183, + "matched_doc_ratio": 0.1, + "original_query_count": 6980, + "filtered_query_count": 739, + "filtered_query_ratio": 0.105874, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 740 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 739, + "full_query_count": 6980 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "84995532aa774083b82a5ee1871c9524", + "source_task_label": "fts_filtered_serial_opensearch_msmarco-large_r90_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_opensearch_msmarco-large_r90_permuted_ossopensearch.json", + "source_sha256": "273610a85902345c8ac567f192050bcda7f7668bcf9d4ed6da1b1b67710352c0", + "source_db_label": "", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_total_series", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.9 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 1667.8223, + "serial_latency_p99": 0.0333, + "serial_latency_p95": 0.0223, + "recall": 0.8559, + "ndcg": 0.5826, + "mrr": 0.5105, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1667.8223, + 1631.7832 + ], + "conc_latency_p99_list": [ + 0.08383034317885171, + 0.09970359897662995 + ], + "conc_latency_p95_list": [ + 0.061932306094968095, + 0.07524768869843683 + ], + "conc_latency_avg_list": [ + 0.03594126598367885, + 0.04894108378701258 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 8399731, + "filter_rate": 0.95, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 442092, + "matched_doc_ratio": 0.05, + "original_query_count": 6980, + "filtered_query_count": 347, + "filtered_query_ratio": 0.049713, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 347 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 347, + "full_query_count": 6980 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "4cac0590f7104d73abb22181858b51be", + "source_task_label": "fts_filtered_serial_opensearch_msmarco-large_r95_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_opensearch_msmarco-large_r95_permuted_ossopensearch.json", + "source_sha256": "958c326fb20b13a2446655f1ba77fd141ed4daa98a84bcc016e398472bb18b4e", + "source_db_label": "", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_total_series", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.95 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 1758.8721, + "serial_latency_p99": 0.0237, + "serial_latency_p95": 0.0205, + "recall": 0.9403, + "ndcg": 0.7033, + "mrr": 0.6373, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 1726.0672, + 1758.8721 + ], + "conc_latency_p99_list": [ + 0.0674230600016017, + 0.07780777791922446 + ], + "conc_latency_p95_list": [ + 0.05345191999731469, + 0.06511080019990913 + ], + "conc_latency_avg_list": [ + 0.03472604073317141, + 0.04543198008032014 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 8753404, + "filter_rate": 0.99, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 88419, + "matched_doc_ratio": 0.01, + "original_query_count": 6980, + "filtered_query_count": 67, + "filtered_query_ratio": 0.009599, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 67 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 67, + "full_query_count": 6980 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "7a432f6e12d34390a787e5ea7727e51b", + "source_task_label": "fts_filtered_serial_opensearch_msmarco-large_r99_permuted", + "source_timestamp": 1785715200.0, + "source_file": "result_20260803_fts_filtered_serial_opensearch_msmarco-large_r99_permuted_ossopensearch.json", + "source_sha256": "48dffb64b34cb1500e65e45183ec2b590158d9df84b20da2249cfe3003356321", + "source_db_label": "", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "OSSOpenSearch", + "db_config": { + "db_label": "", + "version": "", + "note": "{\n \"schema\": \"vdbbench-run-context/v1\",\n \"profile\": \"opensearch/v1\",\n \"backend_version\": \"3.7.0\",\n \"deployment\": {\n \"method\": \"docker\",\n \"config\": {\n \"mode\": \"single_node\"\n }\n },\n \"server_groups\": [\n {\n \"role\": \"opensearch-data-master\",\n \"count\": 1,\n \"jvm_heap_gib\": 30,\n \"hardware\": {\n \"machine_type\": \"i8g.4xlarge\",\n \"cpu_count\": 16,\n \"memory_gib\": 123.54,\n \"storage\": {\n \"type\": \"local_nvme\",\n \"device_count\": 1,\n \"total_gib\": 3492.46,\n \"model\": \"Amazon EC2 NVMe Instance Storage\"\n }\n }\n }\n ],\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_gib\": 61.65,\n \"storage\": {\n \"type\": \"ebs\",\n \"device_count\": 1,\n \"total_gib\": 500,\n \"model\": \"Amazon Elastic Block Store\"\n }\n }\n}", + "index_name": "vdbbench_fts_total_series", + "host": "10.15.9.42", + "port": 9200, + "user": "", + "password": "" + }, + "db_case_config": { + "number_of_shards": 1, + "number_of_replicas": 0, + "refresh_interval": "30s", + "force_merge_enabled": true, + "metric_type": "BM25", + "bm25_k1": null, + "bm25_b": null + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.99 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + } + ], + "file_fmt": "result_{}_{}_{}.json", + "timestamp": 1783468800.0 +} diff --git a/vectordb_bench/results/FullTextSearch/TurboPuffer/result_20260626_fts_standard_turbopuffer.json b/vectordb_bench/results/FullTextSearch/TurboPuffer/result_20260626_fts_standard_turbopuffer.json new file mode 100644 index 000000000..76dd16e30 --- /dev/null +++ b/vectordb_bench/results/FullTextSearch/TurboPuffer/result_20260626_fts_standard_turbopuffer.json @@ -0,0 +1,2312 @@ +{ + "run_id": "fts_standard_turbopuffer", + "task_label": "fts_standard", + "results": [ + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1464.6515, + "optimize_duration": 60.0477, + "load_duration": 1524.6993, + "qps": 1005.2819, + "serial_latency_p99": 0.044, + "serial_latency_p95": 0.0329, + "recall": 0.7745, + "ndcg": 0.6371, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 989.8197, + 982.1386, + 1005.2819 + ], + "conc_latency_p99_list": [ + 1.0339552274506423, + 1.0614141076010128, + 1.067533793257171 + ], + "conc_latency_p95_list": [ + 0.047041563849779776, + 0.05456624450198433, + 0.1361412918016783 + ], + "conc_latency_avg_list": [ + 0.03993696978478835, + 0.06008994437867671, + 0.0777533349887082 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 5233329, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_manifest": { + "bm25": { + "k1": 1.2, + "b": 0.75, + "avgdl": 47.43239876568051 + }, + "analyzer": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "applied_bm25_params": {}, + "unapplied_bm25_params": { + "k1": 1.2, + "b": 0.75, + "avgdl": 47.43239876568051 + }, + "applied_analyzer_params": {}, + "unapplied_analyzer_params": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + }, + "num_per_batch": 1000, + "load_concurrency": 0 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [], + "mrr": 0.7666 + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-06-24T12:21:56.394316", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\"\n },\n \"provenance\": {\n \"capture\": \"retrospective\",\n \"basis\": \"User confirmed filtered and unfiltered FTS cases used the same backend setup.\"\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-hotpotqa-large-c20-b1000-20260624T105757Z", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 72.6864, + "optimize_duration": 60.0531, + "load_duration": 132.7395, + "qps": 1470.4418, + "serial_latency_p99": 0.0481, + "serial_latency_p95": 0.0263, + "recall": 0.8388, + "ndcg": 0.7277, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 1470.4418, + 1430.4126, + 1302.5586 + ], + "conc_latency_p99_list": [ + 0.196402730199046, + 1.0448004564788427, + 1.0593402567028534 + ], + "conc_latency_p95_list": [ + 0.030726359249456436, + 0.03609789424808695, + 0.04879993350186851 + ], + "conc_latency_avg_list": [ + 0.026958557542264637, + 0.04139604535845175, + 0.05992647821000913 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 1000000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_manifest": { + "bm25": { + "k1": 1.2, + "b": 0.75, + "avgdl": 55.803761 + }, + "analyzer": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "applied_bm25_params": {}, + "unapplied_bm25_params": { + "k1": 1.2, + "b": 0.75, + "avgdl": 55.803761 + }, + "applied_analyzer_params": {}, + "unapplied_analyzer_params": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + }, + "num_per_batch": 1000, + "load_concurrency": 0 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [], + "mrr": 0.8579 + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-06-24T12:15:28.328411", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\"\n },\n \"provenance\": {\n \"capture\": \"retrospective\",\n \"basis\": \"User confirmed filtered and unfiltered FTS cases used the same backend setup.\"\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-hotpotqa-medium-c20-b1000-20260624T105757Z", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Medium (1M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 7.1608, + "optimize_duration": 60.0462, + "load_duration": 67.207, + "qps": 1589.0248, + "serial_latency_p99": 0.0413, + "serial_latency_p95": 0.0222, + "recall": 0.9211, + "ndcg": 0.8425, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 1298.648, + 1556.9237, + 1589.0248 + ], + "conc_latency_p99_list": [ + 1.0122341442467582, + 1.040216220889779, + 1.0511543367576086 + ], + "conc_latency_p95_list": [ + 0.0409361274978437, + 0.03276492429940844, + 0.03599922600260471 + ], + "conc_latency_avg_list": [ + 0.030516342558019813, + 0.03799870032145427, + 0.0491667296798954 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 100000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_manifest": { + "bm25": { + "k1": 1.2, + "b": 0.75, + "avgdl": 56.88413 + }, + "analyzer": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "applied_bm25_params": {}, + "unapplied_bm25_params": { + "k1": 1.2, + "b": 0.75, + "avgdl": 56.88413 + }, + "applied_analyzer_params": {}, + "unapplied_analyzer_params": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + }, + "num_per_batch": 1000, + "load_concurrency": 0 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [], + "mrr": 0.9445 + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-06-24T12:09:49.389682", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\"\n },\n \"provenance\": {\n \"capture\": \"retrospective\",\n \"basis\": \"User confirmed filtered and unfiltered FTS cases used the same backend setup.\"\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-hotpotqa-small-c20-b1000-20260624T105757Z", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Small (100K documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 832.0622, + "optimize_duration": 60.0494, + "load_duration": 892.1117, + "qps": 1316.9277, + "serial_latency_p99": 0.042, + "serial_latency_p95": 0.026, + "recall": 0.6245, + "ndcg": 0.2747, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 1316.9277, + 1297.3733, + 1284.6661 + ], + "conc_latency_p99_list": [ + 1.0073482914027407, + 1.0495113781692633, + 1.0606270659984147 + ], + "conc_latency_p95_list": [ + 0.035672420999617316, + 0.04159055870040899, + 0.04908532199988258 + ], + "conc_latency_avg_list": [ + 0.03006028460855096, + 0.045569930162918985, + 0.061195468189531976 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 8841823, + "insert_rows_per_second": 10626.3967, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_manifest": { + "bm25": { + "k1": 1.2, + "b": 0.75, + "avgdl": 57.83363736188793 + }, + "analyzer": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "applied_bm25_params": {}, + "unapplied_bm25_params": { + "k1": 1.2, + "b": 0.75, + "avgdl": 57.83363736188793 + }, + "applied_analyzer_params": {}, + "unapplied_analyzer_params": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + }, + "num_per_batch": 1000, + "load_concurrency": 0 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [], + "mrr": 0.1879 + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-06-24T12:05:15.433938", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\"\n },\n \"provenance\": {\n \"capture\": \"retrospective\",\n \"basis\": \"User confirmed filtered and unfiltered FTS cases used the same backend setup.\"\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-tpuf-msmarco-large-c20-b1000-sdkretry-20260624T094413Z", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 70.9455, + "optimize_duration": 60.0465, + "load_duration": 130.9921, + "qps": 1506.0323, + "serial_latency_p99": 0.0363, + "serial_latency_p95": 0.0227, + "recall": 0.8038, + "ndcg": 0.5226, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 1506.0323, + 1501.8061, + 1494.903 + ], + "conc_latency_p99_list": [ + 0.1576661457991571, + 1.040742779001448, + 1.0539034581720625 + ], + "conc_latency_p95_list": [ + 0.0298914346043603, + 0.03358310699695721, + 0.03905020675083514 + ], + "conc_latency_avg_list": [ + 0.026345458167461474, + 0.03912392624068874, + 0.05247619187674705 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 1000000, + "insert_rows_per_second": 14095.3267, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_manifest": { + "bm25": { + "k1": 1.2, + "b": 0.75, + "avgdl": 57.242324 + }, + "analyzer": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "applied_bm25_params": {}, + "unapplied_bm25_params": { + "k1": 1.2, + "b": 0.75, + "avgdl": 57.242324 + }, + "applied_analyzer_params": {}, + "unapplied_analyzer_params": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + }, + "num_per_batch": 1000, + "load_concurrency": 0 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [], + "mrr": 0.4529 + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-06-24T12:00:41.474589", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\"\n },\n \"provenance\": {\n \"capture\": \"retrospective\",\n \"basis\": \"User confirmed filtered and unfiltered FTS cases used the same backend setup.\"\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-tpuf-msmarco-medium-c20-b1000-cli-20260624T085600Z", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Medium (1M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 7.6649, + "optimize_duration": 60.0529, + "load_duration": 67.7177, + "qps": 1588.1696, + "serial_latency_p99": 0.0379, + "serial_latency_p95": 0.0204, + "recall": 0.9125, + "ndcg": 0.7156, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 1588.1696, + 1437.2379, + 1542.8857 + ], + "conc_latency_p99_list": [ + 0.14128930038219492, + 1.043362593751226, + 1.0526617984978657 + ], + "conc_latency_p95_list": [ + 0.03021625500296065, + 0.03545568099798402, + 0.03865519299870357 + ], + "conc_latency_avg_list": [ + 0.024957518324872457, + 0.041209095071141, + 0.050988707258252025 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 100000, + "insert_rows_per_second": 13046.4846, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_manifest": { + "bm25": { + "k1": 1.2, + "b": 0.75, + "avgdl": 55.01342 + }, + "analyzer": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + } + }, + "applied_bm25_params": {}, + "unapplied_bm25_params": { + "k1": 1.2, + "b": 0.75, + "avgdl": 55.01342 + }, + "applied_analyzer_params": {}, + "unapplied_analyzer_params": { + "filter": [ + "lowercase" + ], + "tokenizer": "standard" + }, + "num_per_batch": 1000, + "load_concurrency": 0 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [], + "mrr": 0.666 + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-06-24T11:56:16.498129", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\"\n },\n \"provenance\": {\n \"capture\": \"retrospective\",\n \"basis\": \"User confirmed filtered and unfiltered FTS cases used the same backend setup.\"\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-tpuf-msmarco-small-c20-b1000-cli-20260624T084228Z", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Small (100K documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 364.2029, + "optimize_duration": 60.0409, + "load_duration": 424.2438, + "qps": 86.5254, + "serial_latency_p99": 0.3979, + "serial_latency_p95": 0.3663, + "recall": 0.8087, + "ndcg": 0.6319, + "mrr": 0.6398, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 37.7946, + 86.5254 + ], + "conc_latency_p99_list": [ + 7.258740147238386, + 6.517188084549162 + ], + "conc_latency_p95_list": [ + 4.526965558199117, + 2.5522155794988066 + ], + "conc_latency_avg_list": [ + 1.2428941904958388, + 0.837548937249788 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 5233329, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 2616664, + "filter_rate": 0.5, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 2616665, + "matched_doc_ratio": 0.5, + "original_query_count": 7405, + "filtered_query_count": 5547, + "filtered_query_ratio": 0.749088, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 6846 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 5547, + "full_query_count": 7405 + }, + "num_per_batch": 1000, + "load_concurrency": 4, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "8c0d2eabf9594bdfb5d9d1957f10ace6", + "source_task_label": "fts_filtered_serial_turbopuffer_hotpotqa-large_r50_permuted", + "source_timestamp": 1785801600.0, + "source_file": "result_20260804_fts_filtered_serial_turbopuffer_hotpotqa-large_r50_permuted_turbopuffer.json", + "source_sha256": "529edcaeea08b03e39532fa8ab30025d640c9834774ed1c16cee2c5a1901243a", + "source_db_label": "turbopuffer-aws-us-west-2-filtered-permuted-serial", + "source_stages": [ + "drop_old", + "load", + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"235ac46886e58d48f9561fb8c39ef913fa3e71d7\",\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"working_tree_clean\": false,\n \"working_tree_scope\": \"pre-existing FullTextSearch result JSON note edits only\"\n },\n \"execution\": {\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\",\n \"namespace_strategy\": \"create_new_per_dataset_and_reuse_across_filter_rates\",\n \"retain_namespace_after_run\": true,\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"sdk_max_retries\": 4,\n \"write_backpressure_enabled\": true,\n \"warmup_seconds_after_load\": 60,\n \"insert_batch_size\": 1000,\n \"load_concurrency\": 1\n },\n \"validation\": {\n \"credentials_stored\": false,\n \"namespace_list_http_status\": 200,\n \"target_namespace_precondition\": \"failed_partial_namespace_exists_and_will_be_reset_once\",\n \"target_namespaces_absent\": false,\n \"verified_at\": \"2026-08-04T03:55:01Z\",\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\"\n ]\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "turbopuffer-aws-us-west-2-filtered-permuted", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"service_version\": \"not_exposed\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"07bcefdbd5b04f51faebfc857219061d46148b6b\",\n \"working_tree_clean\": true\n },\n \"execution\": {\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"write_backpressure_enabled\": true,\n \"sdk_max_retries\": 4,\n \"warmup_seconds_after_load\": 60,\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\"\n },\n \"validation\": {\n \"namespace_list_http_status\": 200,\n \"existing_namespace_metadata_http_status\": 200,\n \"historical_capacity_evidence\": {\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ],\n \"batch_size\": 1000\n },\n \"credentials_stored\": false\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-filter-tpuf-hotpotqa-large-permuted-20260802", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.5 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "drop_old", + "load", + "search_concurrent", + "search_serial" + ], + "load_concurrency": 4 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 147.1902, + "serial_latency_p99": 0.2358, + "serial_latency_p95": 0.2075, + "recall": 0.8378, + "ndcg": 0.658, + "mrr": 0.6328, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 147.1902, + 143.3594 + ], + "conc_latency_p99_list": [ + 2.4505238299202756, + 4.068990539201332 + ], + "conc_latency_p95_list": [ + 1.1879939647995341, + 2.368760299999849 + ], + "conc_latency_avg_list": [ + 0.3934921818363348, + 0.5292828738100664 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 3924996, + "filter_rate": 0.75, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 1308333, + "matched_doc_ratio": 0.25, + "original_query_count": 7405, + "filtered_query_count": 3175, + "filtered_query_ratio": 0.428764, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 3379 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 3175, + "full_query_count": 7405 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "02e928d4d3794fa7a63130805ed5db3b", + "source_task_label": "fts_filtered_serial_turbopuffer_hotpotqa-large_r75_permuted", + "source_timestamp": 1785801600.0, + "source_file": "result_20260804_fts_filtered_serial_turbopuffer_hotpotqa-large_r75_permuted_turbopuffer.json", + "source_sha256": "ac19277d3979b54ad1f290d7be34b85dfbfa240739b7c47e5a6cc0702e5e51f4", + "source_db_label": "turbopuffer-aws-us-west-2-filtered-permuted-serial", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"235ac46886e58d48f9561fb8c39ef913fa3e71d7\",\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"working_tree_clean\": false,\n \"working_tree_scope\": \"pre-existing FullTextSearch result JSON note edits only\"\n },\n \"execution\": {\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\",\n \"namespace_strategy\": \"create_new_per_dataset_and_reuse_across_filter_rates\",\n \"retain_namespace_after_run\": true,\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"sdk_max_retries\": 4,\n \"write_backpressure_enabled\": true,\n \"warmup_seconds_after_load\": 60,\n \"insert_batch_size\": 1000,\n \"load_concurrency\": 1\n },\n \"validation\": {\n \"credentials_stored\": false,\n \"namespace_list_http_status\": 200,\n \"target_namespace_precondition\": \"failed_partial_namespace_exists_and_will_be_reset_once\",\n \"target_namespaces_absent\": false,\n \"verified_at\": \"2026-08-04T03:55:01Z\",\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\"\n ]\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "turbopuffer-aws-us-west-2-filtered-permuted", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"service_version\": \"not_exposed\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"07bcefdbd5b04f51faebfc857219061d46148b6b\",\n \"working_tree_clean\": true\n },\n \"execution\": {\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"write_backpressure_enabled\": true,\n \"sdk_max_retries\": 4,\n \"warmup_seconds_after_load\": 60,\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\"\n },\n \"validation\": {\n \"namespace_list_http_status\": 200,\n \"existing_namespace_metadata_http_status\": 200,\n \"historical_capacity_evidence\": {\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ],\n \"batch_size\": 1000\n },\n \"credentials_stored\": false\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-filter-tpuf-hotpotqa-large-permuted-20260802", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.75 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 296.0612, + "serial_latency_p99": 0.1359, + "serial_latency_p95": 0.1139, + "recall": 0.8764, + "ndcg": 0.7161, + "mrr": 0.6797, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 296.0612, + 295.9694 + ], + "conc_latency_p99_list": [ + 2.3035320769305687, + 2.355359453583151 + ], + "conc_latency_p95_list": [ + 1.0913702148993252, + 1.1043454573995404 + ], + "conc_latency_avg_list": [ + 0.1996025803722017, + 0.2650406779299211 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 4709996, + "filter_rate": 0.9, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 523333, + "matched_doc_ratio": 0.1, + "original_query_count": 7405, + "filtered_query_count": 1367, + "filtered_query_ratio": 0.184605, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 1335 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 1367, + "full_query_count": 7405 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "5136de1fe98b49af853b4a0af228658b", + "source_task_label": "fts_filtered_serial_turbopuffer_hotpotqa-large_r90_permuted", + "source_timestamp": 1785801600.0, + "source_file": "result_20260804_fts_filtered_serial_turbopuffer_hotpotqa-large_r90_permuted_turbopuffer.json", + "source_sha256": "2cad3733352755ac5c977993cffe703cad9b0412358ae9f6c9c73cb80e6014b0", + "source_db_label": "turbopuffer-aws-us-west-2-filtered-permuted-serial", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"235ac46886e58d48f9561fb8c39ef913fa3e71d7\",\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"working_tree_clean\": false,\n \"working_tree_scope\": \"pre-existing FullTextSearch result JSON note edits only\"\n },\n \"execution\": {\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\",\n \"namespace_strategy\": \"create_new_per_dataset_and_reuse_across_filter_rates\",\n \"retain_namespace_after_run\": true,\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"sdk_max_retries\": 4,\n \"write_backpressure_enabled\": true,\n \"warmup_seconds_after_load\": 60,\n \"insert_batch_size\": 1000,\n \"load_concurrency\": 1\n },\n \"validation\": {\n \"credentials_stored\": false,\n \"namespace_list_http_status\": 200,\n \"target_namespace_precondition\": \"failed_partial_namespace_exists_and_will_be_reset_once\",\n \"target_namespaces_absent\": false,\n \"verified_at\": \"2026-08-04T03:55:01Z\",\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\"\n ]\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "turbopuffer-aws-us-west-2-filtered-permuted", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"service_version\": \"not_exposed\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"07bcefdbd5b04f51faebfc857219061d46148b6b\",\n \"working_tree_clean\": true\n },\n \"execution\": {\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"write_backpressure_enabled\": true,\n \"sdk_max_retries\": 4,\n \"warmup_seconds_after_load\": 60,\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\"\n },\n \"validation\": {\n \"namespace_list_http_status\": 200,\n \"existing_namespace_metadata_http_status\": 200,\n \"historical_capacity_evidence\": {\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ],\n \"batch_size\": 1000\n },\n \"credentials_stored\": false\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-filter-tpuf-hotpotqa-large-permuted-20260802", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.9 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 441.3432, + "serial_latency_p99": 0.0853, + "serial_latency_p95": 0.0728, + "recall": 0.8911, + "ndcg": 0.7417, + "mrr": 0.703, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 441.3432, + 437.3237 + ], + "conc_latency_p99_list": [ + 1.1061684508192775, + 2.2820966475379825 + ], + "conc_latency_p95_list": [ + 1.0580634986525184, + 1.0726680879986816 + ], + "conc_latency_avg_list": [ + 0.13406016079070324, + 0.17815990107083907 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 4971662, + "filter_rate": 0.95, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 261667, + "matched_doc_ratio": 0.05, + "original_query_count": 7405, + "filtered_query_count": 698, + "filtered_query_ratio": 0.094261, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 664 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 698, + "full_query_count": 7405 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "10852825e3a84b69ab828b13a1353b97", + "source_task_label": "fts_filtered_serial_turbopuffer_hotpotqa-large_r95_permuted", + "source_timestamp": 1785801600.0, + "source_file": "result_20260804_fts_filtered_serial_turbopuffer_hotpotqa-large_r95_permuted_turbopuffer.json", + "source_sha256": "6398c61fbc97046d03dda2265d76c7acd288c4ba016f6c100757847c6b1e5c3c", + "source_db_label": "turbopuffer-aws-us-west-2-filtered-permuted-serial", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"235ac46886e58d48f9561fb8c39ef913fa3e71d7\",\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"working_tree_clean\": false,\n \"working_tree_scope\": \"pre-existing FullTextSearch result JSON note edits only\"\n },\n \"execution\": {\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\",\n \"namespace_strategy\": \"create_new_per_dataset_and_reuse_across_filter_rates\",\n \"retain_namespace_after_run\": true,\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"sdk_max_retries\": 4,\n \"write_backpressure_enabled\": true,\n \"warmup_seconds_after_load\": 60,\n \"insert_batch_size\": 1000,\n \"load_concurrency\": 1\n },\n \"validation\": {\n \"credentials_stored\": false,\n \"namespace_list_http_status\": 200,\n \"target_namespace_precondition\": \"failed_partial_namespace_exists_and_will_be_reset_once\",\n \"target_namespaces_absent\": false,\n \"verified_at\": \"2026-08-04T03:55:01Z\",\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\"\n ]\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "turbopuffer-aws-us-west-2-filtered-permuted", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"service_version\": \"not_exposed\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"07bcefdbd5b04f51faebfc857219061d46148b6b\",\n \"working_tree_clean\": true\n },\n \"execution\": {\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"write_backpressure_enabled\": true,\n \"sdk_max_retries\": 4,\n \"warmup_seconds_after_load\": 60,\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\"\n },\n \"validation\": {\n \"namespace_list_http_status\": 200,\n \"existing_namespace_metadata_http_status\": 200,\n \"historical_capacity_evidence\": {\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ],\n \"batch_size\": 1000\n },\n \"credentials_stored\": false\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-filter-tpuf-hotpotqa-large-permuted-20260802", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.95 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 780.4231, + "serial_latency_p99": 0.0391, + "serial_latency_p95": 0.0347, + "recall": 0.9048, + "ndcg": 0.8018, + "mrr": 0.7724, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 774.6021, + 780.4231 + ], + "conc_latency_p99_list": [ + 1.069292031079094, + 1.0759943062603998 + ], + "conc_latency_p95_list": [ + 0.085047684600795, + 1.0324142765017315 + ], + "conc_latency_avg_list": [ + 0.07620351681519844, + 0.10069744977102481 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 5180995, + "filter_rate": 0.99, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 52334, + "matched_doc_ratio": 0.01, + "original_query_count": 7405, + "filtered_query_count": 147, + "filtered_query_ratio": 0.019851, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 136 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 147, + "full_query_count": 7405 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "054d1a9c83ec421ea21cd0a0c759639b", + "source_task_label": "fts_filtered_serial_turbopuffer_hotpotqa-large_r99_permuted", + "source_timestamp": 1785801600.0, + "source_file": "result_20260804_fts_filtered_serial_turbopuffer_hotpotqa-large_r99_permuted_turbopuffer.json", + "source_sha256": "36898441804921398245c9a62813ac064c0e5f23c703189bc3d23060c1499bf3", + "source_db_label": "turbopuffer-aws-us-west-2-filtered-permuted-serial", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"235ac46886e58d48f9561fb8c39ef913fa3e71d7\",\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"working_tree_clean\": false,\n \"working_tree_scope\": \"pre-existing FullTextSearch result JSON note edits only\"\n },\n \"execution\": {\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\",\n \"namespace_strategy\": \"create_new_per_dataset_and_reuse_across_filter_rates\",\n \"retain_namespace_after_run\": true,\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"sdk_max_retries\": 4,\n \"write_backpressure_enabled\": true,\n \"warmup_seconds_after_load\": 60,\n \"insert_batch_size\": 1000,\n \"load_concurrency\": 1\n },\n \"validation\": {\n \"credentials_stored\": false,\n \"namespace_list_http_status\": 200,\n \"target_namespace_precondition\": \"failed_partial_namespace_exists_and_will_be_reset_once\",\n \"target_namespaces_absent\": false,\n \"verified_at\": \"2026-08-04T03:55:01Z\",\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\"\n ]\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "turbopuffer-aws-us-west-2-filtered-permuted", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"service_version\": \"not_exposed\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"07bcefdbd5b04f51faebfc857219061d46148b6b\",\n \"working_tree_clean\": true\n },\n \"execution\": {\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"write_backpressure_enabled\": true,\n \"sdk_max_retries\": 4,\n \"warmup_seconds_after_load\": 60,\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\"\n },\n \"validation\": {\n \"namespace_list_http_status\": 200,\n \"existing_namespace_metadata_http_status\": 200,\n \"historical_capacity_evidence\": {\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ],\n \"batch_size\": 1000\n },\n \"credentials_stored\": false\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-filter-tpuf-hotpotqa-large-permuted-20260802", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.99 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1259.9243, + "optimize_duration": 60.0441, + "load_duration": 1319.9684, + "qps": 122.3297, + "serial_latency_p99": 0.2339, + "serial_latency_p95": 0.2155, + "recall": 0.6981, + "ndcg": 0.3471, + "mrr": 0.2577, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 114.6727, + 122.3297 + ], + "conc_latency_p99_list": [ + 3.9483368308206264, + 4.207485043439037 + ], + "conc_latency_p95_list": [ + 2.292980255803195, + 2.3655117287480607 + ], + "conc_latency_avg_list": [ + 0.5048540764116053, + 0.6185035208870814 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 8841823, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 4420911, + "filter_rate": 0.5, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 4420912, + "matched_doc_ratio": 0.5, + "original_query_count": 6980, + "filtered_query_count": 3658, + "filtered_query_ratio": 0.524069, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 3758 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 3658, + "full_query_count": 6980 + }, + "num_per_batch": 1000, + "load_concurrency": 4, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "61ecd49ecda54ad68fe5310dc850c556", + "source_task_label": "fts_filtered_serial_turbopuffer_msmarco-large_r50_permuted", + "source_timestamp": 1785801600.0, + "source_file": "result_20260804_fts_filtered_serial_turbopuffer_msmarco-large_r50_permuted_turbopuffer.json", + "source_sha256": "9a5a1ad4e8fae210e9561e467f262f4cefbff58b500045c113a76213a1599f8a", + "source_db_label": "turbopuffer-aws-us-west-2-filtered-permuted-serial", + "source_stages": [ + "drop_old", + "load", + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"235ac46886e58d48f9561fb8c39ef913fa3e71d7\",\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"working_tree_clean\": false,\n \"working_tree_scope\": \"pre-existing FullTextSearch result JSON note edits only\"\n },\n \"execution\": {\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\",\n \"namespace_strategy\": \"create_new_per_dataset_and_reuse_across_filter_rates\",\n \"retain_namespace_after_run\": true,\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"sdk_max_retries\": 4,\n \"write_backpressure_enabled\": true,\n \"warmup_seconds_after_load\": 60,\n \"insert_batch_size\": 1000,\n \"load_concurrency\": 4\n },\n \"validation\": {\n \"credentials_stored\": false,\n \"namespace_list_http_status\": 200,\n \"target_namespace_precondition\": \"must_not_exist\",\n \"target_namespaces_absent\": true,\n \"verified_at\": \"2026-08-03T12:05:22Z\",\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ]\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "turbopuffer-aws-us-west-2-filtered-permuted", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"service_version\": \"not_exposed\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"07bcefdbd5b04f51faebfc857219061d46148b6b\",\n \"working_tree_clean\": true\n },\n \"execution\": {\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"write_backpressure_enabled\": true,\n \"sdk_max_retries\": 4,\n \"warmup_seconds_after_load\": 60,\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\"\n },\n \"validation\": {\n \"namespace_list_http_status\": 200,\n \"existing_namespace_metadata_http_status\": 200,\n \"historical_capacity_evidence\": {\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ],\n \"batch_size\": 1000\n },\n \"credentials_stored\": false\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-filter-tpuf-msmarco-large-permuted-20260802", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.5 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "drop_old", + "load", + "search_concurrent", + "search_serial" + ], + "load_concurrency": 4 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 113.5809, + "serial_latency_p99": 0.1376, + "serial_latency_p95": 0.1236, + "recall": 0.7629, + "ndcg": 0.4256, + "mrr": 0.3382, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 108.4603, + 113.5809 + ], + "conc_latency_p99_list": [ + 4.06640162431162, + 4.172310605399252 + ], + "conc_latency_p95_list": [ + 2.3489085682997026, + 2.480539664000389 + ], + "conc_latency_avg_list": [ + 0.5278105149102582, + 0.6535439539011187 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 6631367, + "filter_rate": 0.75, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 2210456, + "matched_doc_ratio": 0.25, + "original_query_count": 6980, + "filtered_query_count": 1818, + "filtered_query_ratio": 0.260458, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 1839 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 1818, + "full_query_count": 6980 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "75ec2473225f4497b49bdec6e36b657e", + "source_task_label": "fts_filtered_serial_turbopuffer_msmarco-large_r75_permuted", + "source_timestamp": 1785801600.0, + "source_file": "result_20260804_fts_filtered_serial_turbopuffer_msmarco-large_r75_permuted_turbopuffer.json", + "source_sha256": "76ca2498ee6b124e74c424f03e2f8adb317d376c30e6ea5e8d62162fddb63a58", + "source_db_label": "turbopuffer-aws-us-west-2-filtered-permuted-serial", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"235ac46886e58d48f9561fb8c39ef913fa3e71d7\",\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"working_tree_clean\": false,\n \"working_tree_scope\": \"pre-existing FullTextSearch result JSON note edits only\"\n },\n \"execution\": {\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\",\n \"namespace_strategy\": \"create_new_per_dataset_and_reuse_across_filter_rates\",\n \"retain_namespace_after_run\": true,\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"sdk_max_retries\": 4,\n \"write_backpressure_enabled\": true,\n \"warmup_seconds_after_load\": 60,\n \"insert_batch_size\": 1000,\n \"load_concurrency\": 4\n },\n \"validation\": {\n \"credentials_stored\": false,\n \"namespace_list_http_status\": 200,\n \"target_namespace_precondition\": \"must_not_exist\",\n \"target_namespaces_absent\": true,\n \"verified_at\": \"2026-08-03T12:05:22Z\",\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ]\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "turbopuffer-aws-us-west-2-filtered-permuted", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"service_version\": \"not_exposed\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"07bcefdbd5b04f51faebfc857219061d46148b6b\",\n \"working_tree_clean\": true\n },\n \"execution\": {\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"write_backpressure_enabled\": true,\n \"sdk_max_retries\": 4,\n \"warmup_seconds_after_load\": 60,\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\"\n },\n \"validation\": {\n \"namespace_list_http_status\": 200,\n \"existing_namespace_metadata_http_status\": 200,\n \"historical_capacity_evidence\": {\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ],\n \"batch_size\": 1000\n },\n \"credentials_stored\": false\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-filter-tpuf-msmarco-large-permuted-20260802", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.75 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 174.0195, + "serial_latency_p99": 0.0761, + "serial_latency_p95": 0.0678, + "recall": 0.8369, + "ndcg": 0.5165, + "mrr": 0.4319, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 172.6959, + 174.0195 + ], + "conc_latency_p99_list": [ + 2.431309359999432, + 3.965449629778485 + ], + "conc_latency_p95_list": [ + 1.165103798997734, + 2.323448423299851 + ], + "conc_latency_avg_list": [ + 0.33913986293953957, + 0.4434340066370408 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 7957640, + "filter_rate": 0.9, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 884183, + "matched_doc_ratio": 0.1, + "original_query_count": 6980, + "filtered_query_count": 739, + "filtered_query_ratio": 0.105874, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 740 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 739, + "full_query_count": 6980 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "10fc45c381124295bd4a2dcee19d4f50", + "source_task_label": "fts_filtered_serial_turbopuffer_msmarco-large_r90_permuted", + "source_timestamp": 1785801600.0, + "source_file": "result_20260804_fts_filtered_serial_turbopuffer_msmarco-large_r90_permuted_turbopuffer.json", + "source_sha256": "7b47b06c4a1fbb699fb2fc39a6185410383ac2d511393bc2942cdb3b3d14c9d3", + "source_db_label": "turbopuffer-aws-us-west-2-filtered-permuted-serial", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"235ac46886e58d48f9561fb8c39ef913fa3e71d7\",\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"working_tree_clean\": false,\n \"working_tree_scope\": \"pre-existing FullTextSearch result JSON note edits only\"\n },\n \"execution\": {\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\",\n \"namespace_strategy\": \"create_new_per_dataset_and_reuse_across_filter_rates\",\n \"retain_namespace_after_run\": true,\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"sdk_max_retries\": 4,\n \"write_backpressure_enabled\": true,\n \"warmup_seconds_after_load\": 60,\n \"insert_batch_size\": 1000,\n \"load_concurrency\": 4\n },\n \"validation\": {\n \"credentials_stored\": false,\n \"namespace_list_http_status\": 200,\n \"target_namespace_precondition\": \"must_not_exist\",\n \"target_namespaces_absent\": true,\n \"verified_at\": \"2026-08-03T12:05:22Z\",\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ]\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "turbopuffer-aws-us-west-2-filtered-permuted", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"service_version\": \"not_exposed\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"07bcefdbd5b04f51faebfc857219061d46148b6b\",\n \"working_tree_clean\": true\n },\n \"execution\": {\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"write_backpressure_enabled\": true,\n \"sdk_max_retries\": 4,\n \"warmup_seconds_after_load\": 60,\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\"\n },\n \"validation\": {\n \"namespace_list_http_status\": 200,\n \"existing_namespace_metadata_http_status\": 200,\n \"historical_capacity_evidence\": {\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ],\n \"batch_size\": 1000\n },\n \"credentials_stored\": false\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-filter-tpuf-msmarco-large-permuted-20260802", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.9 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 237.8194, + "serial_latency_p99": 0.0562, + "serial_latency_p95": 0.053, + "recall": 0.853, + "ndcg": 0.5835, + "mrr": 0.512, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 237.8194, + 232.9519 + ], + "conc_latency_p99_list": [ + 2.3618834851501744, + 2.3980035969008893 + ], + "conc_latency_p95_list": [ + 1.116876252649854, + 1.1324147257482764 + ], + "conc_latency_avg_list": [ + 0.24743568345607964, + 0.3360648585017976 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 8399731, + "filter_rate": 0.95, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 442092, + "matched_doc_ratio": 0.05, + "original_query_count": 6980, + "filtered_query_count": 347, + "filtered_query_ratio": 0.049713, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 347 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 347, + "full_query_count": 6980 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "6a289aff37334e4b8146e6279ed4e0e0", + "source_task_label": "fts_filtered_serial_turbopuffer_msmarco-large_r95_permuted", + "source_timestamp": 1785801600.0, + "source_file": "result_20260804_fts_filtered_serial_turbopuffer_msmarco-large_r95_permuted_turbopuffer.json", + "source_sha256": "4ba5221ba7b9294eef13e5adde96d3d16f65e278938e3b44e6b160cd2bd7b25f", + "source_db_label": "turbopuffer-aws-us-west-2-filtered-permuted-serial", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"235ac46886e58d48f9561fb8c39ef913fa3e71d7\",\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"working_tree_clean\": false,\n \"working_tree_scope\": \"pre-existing FullTextSearch result JSON note edits only\"\n },\n \"execution\": {\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\",\n \"namespace_strategy\": \"create_new_per_dataset_and_reuse_across_filter_rates\",\n \"retain_namespace_after_run\": true,\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"sdk_max_retries\": 4,\n \"write_backpressure_enabled\": true,\n \"warmup_seconds_after_load\": 60,\n \"insert_batch_size\": 1000,\n \"load_concurrency\": 4\n },\n \"validation\": {\n \"credentials_stored\": false,\n \"namespace_list_http_status\": 200,\n \"target_namespace_precondition\": \"must_not_exist\",\n \"target_namespaces_absent\": true,\n \"verified_at\": \"2026-08-03T12:05:22Z\",\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ]\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "turbopuffer-aws-us-west-2-filtered-permuted", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"service_version\": \"not_exposed\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"07bcefdbd5b04f51faebfc857219061d46148b6b\",\n \"working_tree_clean\": true\n },\n \"execution\": {\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"write_backpressure_enabled\": true,\n \"sdk_max_retries\": 4,\n \"warmup_seconds_after_load\": 60,\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\"\n },\n \"validation\": {\n \"namespace_list_http_status\": 200,\n \"existing_namespace_metadata_http_status\": 200,\n \"historical_capacity_evidence\": {\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ],\n \"batch_size\": 1000\n },\n \"credentials_stored\": false\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-filter-tpuf-msmarco-large-permuted-20260802", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.95 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 292.0949, + "serial_latency_p99": 0.0592, + "serial_latency_p95": 0.0491, + "recall": 0.9403, + "ndcg": 0.7061, + "mrr": 0.6404, + "conc_num_list": [ + 60, + 80 + ], + "conc_qps_list": [ + 292.0949, + 288.0392 + ], + "conc_latency_p99_list": [ + 2.2982839281494307, + 2.3517081442788914 + ], + "conc_latency_p95_list": [ + 1.097375179249866, + 1.1113469616489966 + ], + "conc_latency_avg_list": [ + 0.20162277745392373, + 0.26459405379449835 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 8753404, + "filter_rate": 0.99, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 88419, + "matched_doc_ratio": 0.01, + "original_query_count": 6980, + "filtered_query_count": 67, + "filtered_query_ratio": 0.009599, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 67 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 67, + "full_query_count": 6980 + }, + "serial_measurement": { + "composed_from_separate_run": true, + "metric_fields": [ + "serial_latency_p99", + "serial_latency_p95", + "recall", + "ndcg", + "mrr" + ], + "source_run_id": "753531e38a8b4fae91339d21ba00d7eb", + "source_task_label": "fts_filtered_serial_turbopuffer_msmarco-large_r99_permuted", + "source_timestamp": 1785801600.0, + "source_file": "result_20260804_fts_filtered_serial_turbopuffer_msmarco-large_r99_permuted_turbopuffer.json", + "source_sha256": "fa8e826ecb4e227835341e63ca2fc006ba68df6f6e2721887ba680db935ba319", + "source_db_label": "turbopuffer-aws-us-west-2-filtered-permuted-serial", + "source_stages": [ + "search_serial" + ], + "source_note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"service_version\": \"not_exposed\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"235ac46886e58d48f9561fb8c39ef913fa3e71d7\",\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"working_tree_clean\": false,\n \"working_tree_scope\": \"pre-existing FullTextSearch result JSON note edits only\"\n },\n \"execution\": {\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\",\n \"namespace_strategy\": \"create_new_per_dataset_and_reuse_across_filter_rates\",\n \"retain_namespace_after_run\": true,\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"sdk_max_retries\": 4,\n \"write_backpressure_enabled\": true,\n \"warmup_seconds_after_load\": 60,\n \"insert_batch_size\": 1000,\n \"load_concurrency\": 4\n },\n \"validation\": {\n \"credentials_stored\": false,\n \"namespace_list_http_status\": 200,\n \"target_namespace_precondition\": \"must_not_exist\",\n \"target_namespaces_absent\": true,\n \"verified_at\": \"2026-08-03T12:05:22Z\",\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ]\n }\n}" + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "turbopuffer-aws-us-west-2-filtered-permuted", + "version": "", + "note": "{\n \"schema\": \"vdbbench-turbopuffer-context/v1\",\n \"backend\": {\n \"name\": \"TurboPuffer\",\n \"deployment\": \"managed_service\",\n \"service_version\": \"not_exposed\",\n \"region\": \"aws-us-west-2\",\n \"api_base_url\": \"https://aws-us-west-2.turbopuffer.com\",\n \"sdk_version\": \"2.6.0\"\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\",\n \"cpu_count\": 8,\n \"memory_kib\": 64648904,\n \"branch\": \"fts_v2_backend\",\n \"commit\": \"07bcefdbd5b04f51faebfc857219061d46148b6b\",\n \"working_tree_clean\": true\n },\n \"execution\": {\n \"pin_namespace\": false,\n \"pin_replicas\": 1,\n \"write_backpressure_enabled\": true,\n \"sdk_max_retries\": 4,\n \"warmup_seconds_after_load\": 60,\n \"credential_delivery\": \"ephemeral mode-600 config outside logs and report bundle; removed by trap\"\n },\n \"validation\": {\n \"namespace_list_http_status\": 200,\n \"existing_namespace_metadata_http_status\": 200,\n \"historical_capacity_evidence\": {\n \"datasets\": [\n \"HotpotQA Large (5.2M documents)\",\n \"MS MARCO Large (8.8M documents)\"\n ],\n \"batch_size\": 1000\n },\n \"credentials_stored\": false\n }\n}", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench-fts-filter-tpuf-msmarco-large-permuted-20260802", + "multitenant_namespace_prefix": "vdbbench_mt_", + "scalar_payload_label_field": "label", + "pin_namespace": false, + "pin_namespace_requested": false, + "pin_replicas": 1, + "pin_timeout": 2700, + "pin_target_namespace_count": 0 + }, + "db_case_config": { + "metric_type": "BM25", + "time_wait_warmup": 60, + "disable_backpressure": false + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.99 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_concurrent", + "search_serial" + ], + "load_concurrency": 0 + }, + "label": ":)" + } + ], + "file_fmt": "result_{}_{}_{}.json", + "timestamp": 1782259200.0 +} diff --git a/vectordb_bench/results/FullTextSearch/ZillizCloud/result_20260626_fts_standard_zillizcloud.json b/vectordb_bench/results/FullTextSearch/ZillizCloud/result_20260626_fts_standard_zillizcloud.json new file mode 100644 index 000000000..6bf69df97 --- /dev/null +++ b/vectordb_bench/results/FullTextSearch/ZillizCloud/result_20260626_fts_standard_zillizcloud.json @@ -0,0 +1,2060 @@ +{ + "run_id": "fts_standard_zillizcloud", + "task_label": "fts_standard", + "results": [ + { + "metrics": { + "max_load_count": 0, + "insert_duration": 92.5443, + "optimize_duration": 81.9515, + "load_duration": 174.4958, + "qps": 1763.3502, + "serial_latency_p99": 0.0331, + "serial_latency_p95": 0.0222, + "recall": 0.7674, + "ndcg": 0.6265, + "mrr": 0.7574, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 1738.3381, + 1760.0975, + 1763.3502 + ], + "conc_latency_p99_list": [ + 0.05301692362816539, + 0.06365443888615119, + 0.07476101434847807 + ], + "conc_latency_p95_list": [ + 0.03960793959267899, + 0.05068928290565963, + 0.061392078457720343 + ], + "conc_latency_avg_list": [ + 0.02283351154340615, + 0.0336278670182451, + 0.044479376343180034 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 5233329, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": {}, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_serial_hotpotqa_permuted_20260803" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 33.6028, + "optimize_duration": 171.902, + "load_duration": 205.5048, + "qps": 4913.8932, + "serial_latency_p99": 0.0107, + "serial_latency_p95": 0.0079, + "recall": 0.8421, + "ndcg": 0.7319, + "mrr": 0.8636, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 4444.7978, + 4823.2308, + 4913.8932 + ], + "conc_latency_p99_list": [ + 0.018735603306413395, + 0.024391331560909748, + 0.02949425330734808 + ], + "conc_latency_p95_list": [ + 0.01459028425233555, + 0.019086839698138645, + 0.023468223997042514 + ], + "conc_latency_avg_list": [ + 0.00892458813977406, + 0.012230295266112242, + 0.01590514856506513 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 1000000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 4 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_15919_hotpotqa_1m" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Medium (1M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 4 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 15.6627, + "optimize_duration": 31.7788, + "load_duration": 47.4414, + "qps": 9577.4013, + "serial_latency_p99": 0.0038, + "serial_latency_p95": 0.0034, + "recall": 0.9225, + "ndcg": 0.8459, + "mrr": 0.9485, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 9125.0541, + 9577.4013, + 9299.4642 + ], + "conc_latency_p99_list": [ + 0.0077219682798022405, + 0.011414849478169344, + 0.014094987509161003 + ], + "conc_latency_p95_list": [ + 0.006174615799682212, + 0.009074159996816888, + 0.011642935798590763 + ], + "conc_latency_avg_list": [ + 0.004340849293562033, + 0.006164715776924379, + 0.008391938989513186 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 100000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 4 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_15919_hotpotqa_100k" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Small (100K documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 4 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 183.6571, + "optimize_duration": 98.8113, + "load_duration": 282.4684, + "qps": 4252.6411, + "serial_latency_p99": 0.0171, + "serial_latency_p95": 0.0116, + "recall": 0.6293, + "ndcg": 0.2765, + "mrr": 0.1889, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 3780.8848, + 4101.5114, + 4252.6411 + ], + "conc_latency_p99_list": [ + 0.02807033602366573, + 0.03447981245903066, + 0.03956317615084118 + ], + "conc_latency_p95_list": [ + 0.01986048770704655, + 0.025059146400599273, + 0.029465016446192746 + ], + "conc_latency_avg_list": [ + 0.010491047885415203, + 0.014407923132888786, + 0.01838277799370675 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 8841823, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": {}, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_serial_msmarco_permuted_20260803" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 37.9759, + "optimize_duration": 116.3291, + "load_duration": 154.305, + "qps": 9734.7457, + "serial_latency_p99": 0.0052, + "serial_latency_p95": 0.0041, + "recall": 0.8091, + "ndcg": 0.5272, + "mrr": 0.4571, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 8342.2067, + 9230.3084, + 9734.7457 + ], + "conc_latency_p99_list": [ + 0.009523463060322682, + 0.012660344481118952, + 0.015673157188284638 + ], + "conc_latency_p95_list": [ + 0.007388512603938574, + 0.0100790631986456, + 0.012480728703667407 + ], + "conc_latency_avg_list": [ + 0.004753512925593361, + 0.006390420499957776, + 0.008009355143216565 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 1000000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 4 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_15919_msmarco_1m" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Medium (1M documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 4 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 21.2111, + "optimize_duration": 39.6819, + "load_duration": 60.893, + "qps": 12134.2059, + "serial_latency_p99": 0.0035, + "serial_latency_p95": 0.003, + "recall": 0.9157, + "ndcg": 0.7206, + "mrr": 0.6713, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 11108.1281, + 12134.2059, + 11328.5193 + ], + "conc_latency_p99_list": [ + 0.00618511842898442, + 0.009046302261704121, + 0.011947811315185387 + ], + "conc_latency_p95_list": [ + 0.0047809927520575, + 0.007048962705448503, + 0.009702080397983082 + ], + "conc_latency_avg_list": [ + 0.0035620799904098437, + 0.0048648263825587625, + 0.006883590285044699 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 100000, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "num_per_batch": 1000, + "load_concurrency": 4 + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_15919_msmarco_100k" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Small (100K documents)", + "payload_profile": "ids_only" + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 4 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 1492.7432, + "serial_latency_p99": 0.0353, + "serial_latency_p95": 0.0253, + "recall": 0.7975, + "ndcg": 0.6176, + "mrr": 0.6255, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 1310.191, + 1469.5941, + 1492.7432 + ], + "conc_latency_p99_list": [ + 0.06350544799934141, + 0.07389547589700673, + 0.08604409836596456 + ], + "conc_latency_p95_list": [ + 0.049314529009279795, + 0.05927793249429669, + 0.07131427159474693 + ], + "conc_latency_avg_list": [ + 0.030282339088862433, + 0.04027559123208463, + 0.05258652822193272 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 2616664, + "filter_rate": 0.5, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 2616665, + "matched_doc_ratio": 0.5, + "original_query_count": 7405, + "filtered_query_count": 5547, + "filtered_query_ratio": 0.749088, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 6846 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 5547, + "full_query_count": 7405 + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_serial_hotpotqa_permuted_20260803" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.5 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 1925.4864, + "serial_latency_p99": 0.0261, + "serial_latency_p95": 0.0193, + "recall": 0.8291, + "ndcg": 0.6445, + "mrr": 0.6195, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 1704.6319, + 1874.4532, + 1925.4864 + ], + "conc_latency_p99_list": [ + 0.04659453985688739, + 0.056227957597002394, + 0.06380992440390398 + ], + "conc_latency_p95_list": [ + 0.03717728605115552, + 0.0455952929914929, + 0.053582699001708534 + ], + "conc_latency_avg_list": [ + 0.023269752517795814, + 0.03155254825551937, + 0.04073994229261837 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 3924996, + "filter_rate": 0.75, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 1308333, + "matched_doc_ratio": 0.25, + "original_query_count": 7405, + "filtered_query_count": 3175, + "filtered_query_ratio": 0.428764, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 3379 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 3175, + "full_query_count": 7405 + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_serial_hotpotqa_permuted_20260803" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.75 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 2484.2534, + "serial_latency_p99": 0.0193, + "serial_latency_p95": 0.0144, + "recall": 0.8687, + "ndcg": 0.7021, + "mrr": 0.6648, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 2200.1066, + 2447.8999, + 2484.2534 + ], + "conc_latency_p99_list": [ + 0.03526244850218063, + 0.04156497399890213, + 0.04930016008423986 + ], + "conc_latency_p95_list": [ + 0.028342882498691324, + 0.03412441574255354, + 0.04170981489733093 + ], + "conc_latency_avg_list": [ + 0.018032777527778057, + 0.024189374054596657, + 0.03155972211155846 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 4709996, + "filter_rate": 0.9, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 523333, + "matched_doc_ratio": 0.1, + "original_query_count": 7405, + "filtered_query_count": 1367, + "filtered_query_ratio": 0.184605, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 1335 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 1367, + "full_query_count": 7405 + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_serial_hotpotqa_permuted_20260803" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.9 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 2771.399, + "serial_latency_p99": 0.0163, + "serial_latency_p95": 0.0123, + "recall": 0.884, + "ndcg": 0.7273, + "mrr": 0.6873, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 2474.7168, + 2727.7045, + 2771.399 + ], + "conc_latency_p99_list": [ + 0.030666672199731695, + 0.037067809136351555, + 0.04432674863608554 + ], + "conc_latency_p95_list": [ + 0.02494138489928446, + 0.030652639707841444, + 0.0372560238080041 + ], + "conc_latency_avg_list": [ + 0.016034226178701837, + 0.02163131208297352, + 0.02828691272543025 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 4971662, + "filter_rate": 0.95, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 261667, + "matched_doc_ratio": 0.05, + "original_query_count": 7405, + "filtered_query_count": 698, + "filtered_query_ratio": 0.094261, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 664 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 698, + "full_query_count": 7405 + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_serial_hotpotqa_permuted_20260803" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.95 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 2887.0488, + "serial_latency_p99": 0.0147, + "serial_latency_p95": 0.0108, + "recall": 0.898, + "ndcg": 0.774, + "mrr": 0.7382, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 2611.7941, + 2842.4523, + 2887.0488 + ], + "conc_latency_p99_list": [ + 0.028886324879422363, + 0.034578777490824, + 0.04206452721206003 + ], + "conc_latency_p95_list": [ + 0.023465643404051658, + 0.028878978548164017, + 0.0356531626493961 + ], + "conc_latency_avg_list": [ + 0.015185543958564984, + 0.02079020171914545, + 0.027100502311054302 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 5180995, + "filter_rate": 0.99, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 3234376, + "filter_id_offset": 1082441, + "matched_doc_count": 52334, + "matched_doc_ratio": 0.01, + "original_query_count": 7405, + "filtered_query_count": 147, + "filtered_query_ratio": 0.019851, + "original_relevant_doc_count": 13783, + "filtered_relevant_doc_count": 136 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 147, + "full_query_count": 7405 + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_serial_hotpotqa_permuted_20260803" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "HotpotQA Large (5.2M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.99 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 2157.5384, + "serial_latency_p99": 0.0251, + "serial_latency_p95": 0.0189, + "recall": 0.7044, + "ndcg": 0.3475, + "mrr": 0.2565, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 1602.7776, + 1965.32, + 2157.5384 + ], + "conc_latency_p99_list": [ + 0.04808061543459191, + 0.05590218743600418, + 0.06283525501348776 + ], + "conc_latency_p95_list": [ + 0.038264308209181766, + 0.044974703500338366, + 0.05120332330261589 + ], + "conc_latency_avg_list": [ + 0.024761475757823056, + 0.03012121981174715, + 0.036368580013719426 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 4420911, + "filter_rate": 0.5, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 4420912, + "matched_doc_ratio": 0.5, + "original_query_count": 6980, + "filtered_query_count": 3658, + "filtered_query_ratio": 0.524069, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 3758 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 3658, + "full_query_count": 6980 + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_serial_msmarco_permuted_20260803" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.5 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 2761.6694, + "serial_latency_p99": 0.0192, + "serial_latency_p95": 0.0146, + "recall": 0.7668, + "ndcg": 0.4252, + "mrr": 0.3365, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 2105.0956, + 2509.0089, + 2761.6694 + ], + "conc_latency_p99_list": [ + 0.03667745854880194, + 0.043968265774310566, + 0.04997027571342189 + ], + "conc_latency_p95_list": [ + 0.029508575000363628, + 0.03553523525770286, + 0.04100487310497556 + ], + "conc_latency_avg_list": [ + 0.018847058349541947, + 0.023569575725606327, + 0.028409127438431932 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 6631367, + "filter_rate": 0.75, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 2210456, + "matched_doc_ratio": 0.25, + "original_query_count": 6980, + "filtered_query_count": 1818, + "filtered_query_ratio": 0.260458, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 1839 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 1818, + "full_query_count": 6980 + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_serial_msmarco_permuted_20260803" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.75 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 3467.3872, + "serial_latency_p99": 0.0139, + "serial_latency_p95": 0.0113, + "recall": 0.8383, + "ndcg": 0.5192, + "mrr": 0.435, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 2695.8315, + 3198.641, + 3467.3872 + ], + "conc_latency_p99_list": [ + 0.028783008844475263, + 0.03443616359960289, + 0.03963516379910289 + ], + "conc_latency_p95_list": [ + 0.023086678193067197, + 0.02811312419653404, + 0.032976010798302015 + ], + "conc_latency_avg_list": [ + 0.014714017594863122, + 0.018505583353593157, + 0.022566899350545157 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 7957640, + "filter_rate": 0.9, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 884183, + "matched_doc_ratio": 0.1, + "original_query_count": 6980, + "filtered_query_count": 739, + "filtered_query_ratio": 0.105874, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 740 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 739, + "full_query_count": 6980 + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_serial_msmarco_permuted_20260803" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.9 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 3866.9025, + "serial_latency_p99": 0.0126, + "serial_latency_p95": 0.01, + "recall": 0.8473, + "ndcg": 0.5792, + "mrr": 0.5077, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 3009.9118, + 3519.8953, + 3866.9025 + ], + "conc_latency_p99_list": [ + 0.02564647176885045, + 0.03126390196281136, + 0.03594922040065286 + ], + "conc_latency_p95_list": [ + 0.020645123507711104, + 0.02540512894993297, + 0.02950239399797283 + ], + "conc_latency_avg_list": [ + 0.013172088723884543, + 0.01679307632555934, + 0.02024829934249553 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 8399731, + "filter_rate": 0.95, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 442092, + "matched_doc_ratio": 0.05, + "original_query_count": 6980, + "filtered_query_count": 347, + "filtered_query_ratio": 0.049713, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 347 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 347, + "full_query_count": 6980 + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_serial_msmarco_permuted_20260803" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.95 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 4028.3996, + "serial_latency_p99": 0.0246, + "serial_latency_p95": 0.0098, + "recall": 0.9403, + "ndcg": 0.6958, + "mrr": 0.628, + "conc_num_list": [ + 40, + 60, + 80 + ], + "conc_qps_list": [ + 3205.3504, + 3736.9223, + 4028.3996 + ], + "conc_latency_p99_list": [ + 0.023615233003511128, + 0.02917098919919226, + 0.034491611427802125 + ], + "conc_latency_p95_list": [ + 0.019226595002692193, + 0.02385359159961807, + 0.028303666404826795 + ], + "conc_latency_avg_list": [ + 0.012365652090037205, + 0.015849408703119124, + 0.01946939073238616 + ], + "payload_profile": "ids_only", + "payload_estimated_bytes_per_query": 2000, + "inserted_count": 0, + "insert_rows_per_second": 0.0, + "insert_completion_seconds": 0.0, + "searchable_after_insert_seconds": 0.0, + "indexed_after_searchable_seconds": 0.0, + "additional_parameters": { + "fts_filter": { + "filter_type": "NumGE", + "filter_field": "filter_id", + "filter_value": 8753404, + "filter_rate": 0.99, + "filter_id_distribution": "affine_permutation_v1", + "filter_id_multiplier": 5464547, + "filter_id_offset": 1684563, + "matched_doc_count": 88419, + "matched_doc_ratio": 0.01, + "original_query_count": 6980, + "filtered_query_count": 67, + "filtered_query_ratio": 0.009599, + "original_relevant_doc_count": 7433, + "filtered_relevant_doc_count": 67 + }, + "fts_recall": { + "skipped": false, + "reason": null, + "serial_query_count": 67, + "full_query_count": 6980 + } + }, + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "task15919-perf-8cu-oneseg-26-retest", + "version": "", + "note": "{\n \"schema\": \"vdbbench-zilliz-cloud-context/v1\",\n \"backend\": {\n \"name\": \"ZillizCloud\",\n \"region\": \"aws-us-west-2\",\n \"cu\": 8,\n \"replica_count\": 1,\n \"instance_type\": \"performance\",\n \"deployment_method\": \"kubernetes\",\n \"deployment_mode\": \"standalone\",\n \"server_role\": \"milvus-standalone-r1\",\n \"server_count\": 1\n },\n \"client\": {\n \"machine_type\": \"i8g.2xlarge\"\n },\n \"evidence\": {\n \"source\": \"User-confirmed Task 15919 deployment shape; one persistent segment per benchmark collection validated by run preflight and post-run inventory\",\n \"credential_storage\": \"external mode-600 secret file removed by runner cleanup\"\n }\n}", + "uri": "**********", + "user": "root", + "password": "**********", + "token": "", + "num_shards": 1, + "collection_name": "vdbbench_fts_serial_msmarco_permuted_20260803" + }, + "db_case_config": { + "index_type": "AUTOINDEX", + "metric_type": "BM25", + "inverted_index_algo": "DAAT_MAXSCORE", + "bm25_k1": null, + "bm25_b": null, + "analyzer_tokenizer": "standard", + "analyzer_enable_lowercase": true, + "analyzer_max_token_length": null, + "analyzer_stop_words": null, + "drop_ratio_search": null, + "level": 1 + }, + "case_config": { + "case_id": 503, + "custom_case": { + "dataset_with_size_type": "MS MARCO Large (8.8M documents)", + "payload_profile": "ids_only", + "filter_rate": 0.99 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600, + "serial_cooldown": 0.0 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + } + ], + "file_fmt": "result_{}_{}_{}.json", + "timestamp": 1786060800.0 +} diff --git a/vectordb_bench/results/Milvus/result_20260403_standard_milvus.json b/vectordb_bench/results/Milvus/result_20260403_standard_milvus.json new file mode 100644 index 000000000..56219eb1d --- /dev/null +++ b/vectordb_bench/results/Milvus/result_20260403_standard_milvus.json @@ -0,0 +1,4074 @@ +{ + "run_id": "c11e83b51ff14060a08f06d58f801214", + "task_label": "standard_20260403", + "results": [ + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1444.847, + "optimize_duration": 7859.1353, + "load_duration": 9303.9823, + "qps": 3917.2035, + "serial_latency_p99": 0.0024, + "serial_latency_p95": 0.0023, + "recall": 0.9203, + "ndcg": 0.9238, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 467.4942, + 1828.2004, + 2454.4495, + 2976.5539, + 3178.6052, + 3494.4787, + 3755.8025, + 3917.2035 + ], + "conc_latency_p99_list": [ + 0.0025411477516172455, + 0.0033511776782688685, + 0.005052069290541106, + 0.011894863871420993, + 0.016992485875962308, + 0.020195355401374387, + 0.026863074758439298, + 0.03365033164591297 + ], + "conc_latency_p95_list": [ + 0.0023739541989925782, + 0.00314145510783419, + 0.004699915152013999, + 0.009829005991923623, + 0.014131764802732504, + 0.017079706999356854, + 0.022949057801451987, + 0.02835416550078662 + ], + "conc_latency_avg_list": [ + 0.002136165356290855, + 0.002730801989459182, + 0.004067402028788586, + 0.006701902388970856, + 0.009401196782828802, + 0.011384491649831373, + 0.015778383442210466, + 0.020076245655094027 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 100, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1444.847, + "optimize_duration": 7859.1353, + "load_duration": 9303.9823, + "qps": 3628.8527, + "serial_latency_p99": 0.0026, + "serial_latency_p95": 0.0024, + "recall": 0.9318, + "ndcg": 0.9346, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 456.622, + 1760.1234, + 2305.1866, + 2811.6811, + 3062.7639, + 3254.2934, + 3483.1948, + 3628.8527 + ], + "conc_latency_p99_list": [ + 0.0025775732805777807, + 0.003496330338239204, + 0.005392098429438191, + 0.012580982464569393, + 0.01744679359835574, + 0.02114546248485566, + 0.02862863955655484, + 0.03660006429607165 + ], + "conc_latency_p95_list": [ + 0.002428409402637044, + 0.003272143194044474, + 0.005011953243229073, + 0.010387669454212298, + 0.01447147800354287, + 0.017890810401149794, + 0.023953950349823568, + 0.030202264491526864 + ], + "conc_latency_avg_list": [ + 0.002187054468852311, + 0.002836289985030498, + 0.004330527318313066, + 0.007095944638723138, + 0.009756847450075634, + 0.012226813030036315, + 0.017037973309470753, + 0.021662613936531003 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 120, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1444.847, + "optimize_duration": 7859.1353, + "load_duration": 9303.9823, + "qps": 3250.1112, + "serial_latency_p99": 0.0027, + "serial_latency_p95": 0.0025, + "recall": 0.9443, + "ndcg": 0.9463, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 434.6843, + 1686.1263, + 2163.5936, + 2555.03, + 2793.5899, + 2921.7137, + 3130.5251, + 3250.1112 + ], + "conc_latency_p99_list": [ + 0.0027417352329939604, + 0.003703351480362472, + 0.005958503000438213, + 0.013709455646312565, + 0.01829364382574568, + 0.022580389242502868, + 0.03056172190743382, + 0.037927262283337766 + ], + "conc_latency_p95_list": [ + 0.0025719269993714987, + 0.0034471726998162922, + 0.005409308793605305, + 0.011277809000603156, + 0.015221869490051177, + 0.019072343403240672, + 0.02558471505108173, + 0.03225500610860763 + ], + "conc_latency_avg_list": [ + 0.002297469730699608, + 0.0029608701952901183, + 0.004614075755959777, + 0.007808470701124476, + 0.010702304970719298, + 0.013606579627839316, + 0.018957654525715156, + 0.02420684028195088 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 150, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1444.847, + "optimize_duration": 7859.1353, + "load_duration": 9303.9823, + "qps": 2762.4144, + "serial_latency_p99": 0.0031, + "serial_latency_p95": 0.0029, + "recall": 0.9556, + "ndcg": 0.9567, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 395.9308, + 1515.3208, + 1959.8545, + 2234.9654, + 2427.3893, + 2537.495, + 2657.5964, + 2762.4144 + ], + "conc_latency_p99_list": [ + 0.003096595037495717, + 0.00413693475740729, + 0.007250679123098961, + 0.01500825069088023, + 0.01980871459498304, + 0.024630421155015937, + 0.033659081743244314, + 0.041983861521002835 + ], + "conc_latency_p95_list": [ + 0.0028621868113987148, + 0.003837919446232263, + 0.006172719193273224, + 0.012415598499501357, + 0.0168672572079231, + 0.021068831244338074, + 0.028828703101316914, + 0.03613693019142374 + ], + "conc_latency_avg_list": [ + 0.002522697462250955, + 0.0032950399942241306, + 0.0050943009057731, + 0.008926197356642889, + 0.012320074532162776, + 0.01567786154805744, + 0.022343385743465827, + 0.028501836863104958 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 200, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1444.847, + "optimize_duration": 7859.1353, + "load_duration": 9303.9823, + "qps": 2384.6245, + "serial_latency_p99": 0.0032, + "serial_latency_p95": 0.003, + "recall": 0.9627, + "ndcg": 0.9632, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 368.492, + 1356.984, + 1780.9947, + 1978.8754, + 2086.4087, + 2180.2788, + 2347.7647, + 2384.6245 + ], + "conc_latency_p99_list": [ + 0.0033670899452408775, + 0.004818720840266903, + 0.008331618664960839, + 0.01588158588972873, + 0.02218642996114793, + 0.02756158859701827, + 0.036660355595813585, + 0.047074296680802936 + ], + "conc_latency_p95_list": [ + 0.0031033190454763816, + 0.004360820600413717, + 0.00704125490374281, + 0.013423533007153307, + 0.019178588101203785, + 0.023883526999270543, + 0.03231190550286556, + 0.041070348300854674 + ], + "conc_latency_avg_list": [ + 0.0027103553220750067, + 0.0036796698451320694, + 0.005606053561698588, + 0.010084455073144883, + 0.01433518456445194, + 0.0182535198619555, + 0.025308713260092305, + 0.03299686872047841 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 250, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1444.847, + "optimize_duration": 7859.1353, + "load_duration": 9303.9823, + "qps": 2134.1717, + "serial_latency_p99": 0.0038, + "serial_latency_p95": 0.0036, + "recall": 0.9671, + "ndcg": 0.9672, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 342.9302, + 1297.1692, + 1605.3373, + 1723.4931, + 1874.7201, + 1950.9837, + 2026.1279, + 2134.1717 + ], + "conc_latency_p99_list": [ + 0.003612513002881314, + 0.005075874237372772, + 0.009592544495098993, + 0.01732624325857614, + 0.02383941526291892, + 0.02994993883999997, + 0.04171322006412084, + 0.052276343395351435 + ], + "conc_latency_p95_list": [ + 0.00337228730058996, + 0.00455044719419675, + 0.008192990009411006, + 0.015064282899402313, + 0.02108702180557884, + 0.02638680679956451, + 0.036932101499405685, + 0.04595883999718353 + ], + "conc_latency_avg_list": [ + 0.0029124934026024873, + 0.0038495151919032532, + 0.0062189602467817824, + 0.011577066999218889, + 0.01595138406671783, + 0.02037925821852399, + 0.029283526202598515, + 0.03685219368742522 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 300, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1444.847, + "optimize_duration": 7859.1353, + "load_duration": 9303.9823, + "qps": 1641.3478, + "serial_latency_p99": 0.0041, + "serial_latency_p95": 0.0039, + "recall": 0.9729, + "ndcg": 0.9726, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 300.7136, + 1134.4852, + 1338.9285, + 1408.0122, + 1523.2338, + 1540.9389, + 1631.3282, + 1641.3478 + ], + "conc_latency_p99_list": [ + 0.004302493912400677, + 0.006291621240816314, + 0.011818032589217181, + 0.020626583240227772, + 0.028327662804658774, + 0.03604436940222513, + 0.0508815360846347, + 0.06704054794870894 + ], + "conc_latency_p95_list": [ + 0.003943610056012403, + 0.0053649904970370695, + 0.010212573444005101, + 0.018129582199617286, + 0.02555973254638957, + 0.03308224900683854, + 0.04562628499115817, + 0.05833644095400814 + ], + "conc_latency_avg_list": [ + 0.0033214706270654664, + 0.004401974502845957, + 0.00745933768945177, + 0.014171396440932295, + 0.0196399944581788, + 0.025851743210982894, + 0.03640642504236049, + 0.048033687367785266 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 400, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1444.847, + "optimize_duration": 7859.1353, + "load_duration": 9303.9823, + "qps": 1488.5841, + "serial_latency_p99": 0.0047, + "serial_latency_p95": 0.0043, + "recall": 0.9764, + "ndcg": 0.976, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 259.0154, + 1019.3401, + 1241.5382, + 1301.0531, + 1358.779, + 1399.0002, + 1425.3615, + 1488.5841 + ], + "conc_latency_p99_list": [ + 0.004889184155326802, + 0.007301273914636109, + 0.01256226149998838, + 0.02185779360006564, + 0.03058185266534565, + 0.03868378391998703, + 0.0577071424780297, + 0.07162654809217199 + ], + "conc_latency_p95_list": [ + 0.004537451700889505, + 0.006121107403305359, + 0.010819275506946724, + 0.019440887503151316, + 0.02824652445488027, + 0.035727200949622784, + 0.05152663829067023, + 0.0646723014942836 + ], + "conc_latency_avg_list": [ + 0.0038569616304989004, + 0.0048995726665189525, + 0.008043409835523815, + 0.015340665489496967, + 0.022014652133720863, + 0.02845767912486287, + 0.04164023567491625, + 0.05289991318053246 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 500, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1459.7483, + "optimize_duration": 7524.2304, + "load_duration": 8983.9787, + "qps": 2747.3167, + "serial_latency_p99": 0.0033, + "serial_latency_p95": 0.003, + "recall": 0.9204, + "ndcg": 0.9262, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 412.8127, + 1516.5887, + 1934.7028, + 2345.2563, + 2531.5491, + 2627.1159, + 2747.3167, + 2733.8527 + ], + "conc_latency_p99_list": [ + 0.0030919767648447303, + 0.004120720853097738, + 0.006685402100338251, + 0.014304348317091375, + 0.019310851126065252, + 0.024482081828900833, + 0.03435190891788809, + 0.04423686145673856 + ], + "conc_latency_p95_list": [ + 0.0028119500013417563, + 0.003827417498541763, + 0.006025877799402224, + 0.011986060402705334, + 0.016484533800394274, + 0.020710799152220714, + 0.028733705398917665, + 0.037459268098609756 + ], + "conc_latency_avg_list": [ + 0.002419260628861797, + 0.003292419872587543, + 0.005160383001607833, + 0.00850752764269055, + 0.011804369542313493, + 0.01513907190518365, + 0.021623125977333776, + 0.028768996633767287 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 100, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1459.7483, + "optimize_duration": 7524.2304, + "load_duration": 8983.9787, + "qps": 2514.4481, + "serial_latency_p99": 0.0032, + "serial_latency_p95": 0.003, + "recall": 0.9303, + "ndcg": 0.9357, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 376.3101, + 1413.4044, + 1804.5963, + 2063.4126, + 2224.2985, + 2285.9557, + 2428.4015, + 2514.4481 + ], + "conc_latency_p99_list": [ + 0.003350270938608448, + 0.004419036731342202, + 0.007495493090173118, + 0.01604453914129406, + 0.021470454052177963, + 0.027058087129116764, + 0.03722573504091981, + 0.048193111100408685 + ], + "conc_latency_p95_list": [ + 0.003067891455066274, + 0.004094811100731022, + 0.006531247201928636, + 0.013379140298275157, + 0.018289522749910248, + 0.0233742457050539, + 0.03192761289792543, + 0.040280850498675136 + ], + "conc_latency_avg_list": [ + 0.002653342368273733, + 0.0035319332925499757, + 0.005532113228559582, + 0.009670992982743579, + 0.013421869019516723, + 0.017384391084778364, + 0.024445988357111505, + 0.03128177971695877 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 120, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1459.7483, + "optimize_duration": 7524.2304, + "load_duration": 8983.9787, + "qps": 2177.2345, + "serial_latency_p99": 0.0034, + "serial_latency_p95": 0.0031, + "recall": 0.9408, + "ndcg": 0.9456, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 354.703, + 1270.9673, + 1660.8118, + 1799.9874, + 1911.9995, + 2018.5829, + 2119.3303, + 2177.2345 + ], + "conc_latency_p99_list": [ + 0.0035591462109005084, + 0.0051245430819108154, + 0.008888348671316635, + 0.017485948742978506, + 0.023954762732464586, + 0.02942745841770374, + 0.04045989539969013, + 0.052306493496216695 + ], + "conc_latency_p95_list": [ + 0.003282636954463669, + 0.004633509999257512, + 0.0075649314512702395, + 0.014845420597703196, + 0.02076956499913649, + 0.025784255946928167, + 0.035505612393899356, + 0.045118153299335974 + ], + "conc_latency_avg_list": [ + 0.002815711151857267, + 0.003928179410303787, + 0.006011238757150647, + 0.011084966830100315, + 0.015638666422418603, + 0.019708495546663755, + 0.02799275543572887, + 0.03611913807969698 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 150, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1459.7483, + "optimize_duration": 7524.2304, + "load_duration": 8983.9787, + "qps": 1833.2575, + "serial_latency_p99": 0.0039, + "serial_latency_p95": 0.0035, + "recall": 0.951, + "ndcg": 0.9555, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 329.7676, + 1176.835, + 1426.5695, + 1546.3292, + 1656.2697, + 1716.0803, + 1790.6121, + 1833.2575 + ], + "conc_latency_p99_list": [ + 0.003868887719290797, + 0.005920497829865769, + 0.01097595489642117, + 0.01919553377847477, + 0.02603815700131236, + 0.03326150514345494, + 0.04663379819947293, + 0.05838948019809325 + ], + "conc_latency_p95_list": [ + 0.003534871701413067, + 0.005110333902484853, + 0.009471108402794925, + 0.01673110910487594, + 0.023566940006276127, + 0.02969893909685197, + 0.04118938999890815, + 0.05255015200236812 + ], + "conc_latency_avg_list": [ + 0.0030290844068619504, + 0.004243610521352684, + 0.007000278697486464, + 0.012908119052890047, + 0.018050427070200076, + 0.023198493359582885, + 0.03316593436696198, + 0.04298056582898082 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 200, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1459.7483, + "optimize_duration": 7524.2304, + "load_duration": 8983.9787, + "qps": 1552.4803, + "serial_latency_p99": 0.004, + "serial_latency_p95": 0.0037, + "recall": 0.9565, + "ndcg": 0.9605, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 303.2446, + 1078.6424, + 1299.206, + 1360.6235, + 1425.268, + 1465.3767, + 1520.0788, + 1552.4803 + ], + "conc_latency_p99_list": [ + 0.0040712328813970085, + 0.006725932629851741, + 0.012099390296789348, + 0.021067992354510352, + 0.02953478858660674, + 0.037649333699373524, + 0.05402578063920373, + 0.07030989054946984 + ], + "conc_latency_p95_list": [ + 0.0038339799008099357, + 0.005704106903795035, + 0.01050544159807032, + 0.01850984884877107, + 0.02700225284861517, + 0.034401998404064216, + 0.04840153990226099, + 0.06195298564853147 + ], + "conc_latency_avg_list": [ + 0.003293997160806082, + 0.004629127288807816, + 0.00768599232973781, + 0.014665701743116042, + 0.02097794137202591, + 0.027194933866063295, + 0.03908773235524631, + 0.050673599669096146 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 250, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1459.7483, + "optimize_duration": 7524.2304, + "load_duration": 8983.9787, + "qps": 1355.3121, + "serial_latency_p99": 0.0044, + "serial_latency_p95": 0.0042, + "recall": 0.9602, + "ndcg": 0.964, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 279.2851, + 975.7699, + 1150.3744, + 1187.2494, + 1247.0213, + 1283.8337, + 1302.6721, + 1355.3121 + ], + "conc_latency_p99_list": [ + 0.004451236832683208, + 0.0076792708405992016, + 0.01329627803701441, + 0.023810231103198035, + 0.033509768832373055, + 0.04278365236037641, + 0.06259672742118709, + 0.08097375019555315 + ], + "conc_latency_p95_list": [ + 0.004183817799275858, + 0.006473346400889567, + 0.011516073600796517, + 0.0212969502517808, + 0.030741239200142444, + 0.03883912599849282, + 0.056272352899395625, + 0.07115194579964736 + ], + "conc_latency_avg_list": [ + 0.003576748584184128, + 0.005118136384013237, + 0.008682482330775591, + 0.0168131587141993, + 0.023978672854972592, + 0.031007006045867456, + 0.04562714668667405, + 0.058088526012853484 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 300, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1459.7483, + "optimize_duration": 7524.2304, + "load_duration": 8983.9787, + "qps": 1079.2123, + "serial_latency_p99": 0.0053, + "serial_latency_p95": 0.0049, + "recall": 0.9648, + "ndcg": 0.9686, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 241.6992, + 799.4681, + 939.7848, + 975.2814, + 1001.75, + 1029.2059, + 1065.2548, + 1079.2123 + ], + "conc_latency_p99_list": [ + 0.005288620950886979, + 0.009748364380211563, + 0.015409245888222355, + 0.028667413447983563, + 0.04076468282910357, + 0.05202346699952616, + 0.07527647967857774, + 0.09870467029977587 + ], + "conc_latency_p95_list": [ + 0.004910965752060292, + 0.008243431097071152, + 0.013543863647282705, + 0.025803527399330048, + 0.03817865899909521, + 0.048153773248486686, + 0.06777279375382932, + 0.08822071449685609 + ], + "conc_latency_avg_list": [ + 0.004132684600647634, + 0.006247207634111248, + 0.010625959006772904, + 0.02046250279903178, + 0.02985517599151899, + 0.0386749397755516, + 0.055773659676497576, + 0.07301672729002262 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 400, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 1459.7483, + "optimize_duration": 7524.2304, + "load_duration": 8983.9787, + "qps": 876.5772, + "serial_latency_p99": 0.0063, + "serial_latency_p95": 0.0059, + "recall": 0.9676, + "ndcg": 0.9713, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 213.9083, + 704.2348, + 797.531, + 821.1706, + 825.533, + 797.2598, + 858.8645, + 876.5772 + ], + "conc_latency_p99_list": [ + 0.0060647999984212225, + 0.01099963705062691, + 0.017907913918024848, + 0.03398729390319204, + 0.049453794501459925, + 0.06846078443864825, + 0.09411621719773387, + 0.12295867912092945 + ], + "conc_latency_p95_list": [ + 0.005586325001786463, + 0.009474276846958667, + 0.015768809196015348, + 0.03062209299969254, + 0.046334437996847555, + 0.06321956860047066, + 0.08318591200077208, + 0.10695994940178935 + ], + "conc_latency_avg_list": [ + 0.004670567833335547, + 0.007091383770451675, + 0.01252405204148396, + 0.02430557731179879, + 0.036228697626746305, + 0.04987607783688658, + 0.06916092553517646, + 0.09005697652328008 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 500, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 129.799, + "optimize_duration": 152.2479, + "load_duration": 282.0468, + "qps": 10663.1231, + "serial_latency_p99": 0.002, + "serial_latency_p95": 0.0018, + "recall": 0.8405, + "ndcg": 0.8674, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 592.3235, + 2657.0693, + 4857.6653, + 7731.1954, + 9035.8688, + 9746.5046, + 10547.8051, + 10663.1231 + ], + "conc_latency_p99_list": [ + 0.0019481061986880378, + 0.002309793634340169, + 0.0025996991485590097, + 0.003907884397631278, + 0.005842388768505771, + 0.007729610755923204, + 0.011498715590278154, + 0.01563908824173267 + ], + "conc_latency_p95_list": [ + 0.0018476900004316121, + 0.0021391708025475962, + 0.0023516209941590203, + 0.0031451117574761156, + 0.004541207106376533, + 0.006081955801346338, + 0.008940340152912542, + 0.012104344801628029 + ], + "conc_latency_avg_list": [ + 0.0016856015045043139, + 0.0018777718208453423, + 0.0020532305285396453, + 0.0025771131947367317, + 0.0033031913740212363, + 0.0040746336526161255, + 0.005611474910509973, + 0.007361856948830913 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 100, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 129.799, + "optimize_duration": 152.2479, + "load_duration": 282.0468, + "qps": 10333.9072, + "serial_latency_p99": 0.002, + "serial_latency_p95": 0.0019, + "recall": 0.889, + "ndcg": 0.9058, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 568.5874, + 2594.6923, + 4693.0426, + 7429.976, + 8564.4439, + 9136.6911, + 9985.356, + 10333.9072 + ], + "conc_latency_p99_list": [ + 0.0020693473634310062, + 0.0023700819991063315, + 0.0026944835495669377, + 0.004012327503005508, + 0.006051957674062573, + 0.008206313409027643, + 0.012019852105004216, + 0.015895357320114278 + ], + "conc_latency_p95_list": [ + 0.0019474557979265227, + 0.0021944244945188984, + 0.002450034000503365, + 0.003276585503044771, + 0.004752537995955206, + 0.006455344991991296, + 0.009426155498658773, + 0.012384038396703547 + ], + "conc_latency_avg_list": [ + 0.0017559580574677634, + 0.0019229909223785742, + 0.0021257891514691474, + 0.0026812402175934932, + 0.0034838524012537763, + 0.00433970032247638, + 0.005933638012347279, + 0.007582591068540877 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 100, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 1.2 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 129.799, + "optimize_duration": 152.2479, + "load_duration": 282.0468, + "qps": 9575.6863, + "serial_latency_p99": 0.0023, + "serial_latency_p95": 0.0021, + "recall": 0.9189, + "ndcg": 0.93, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 535.4941, + 2437.6236, + 4429.593, + 6980.413, + 8035.6356, + 8594.0728, + 9041.3709, + 9575.6863 + ], + "conc_latency_p99_list": [ + 0.002218716748757288, + 0.002563155780226227, + 0.0028770195422111954, + 0.004238974919717297, + 0.006498919794103134, + 0.008666419192886682, + 0.013239830499514937, + 0.016983359853475096 + ], + "conc_latency_p95_list": [ + 0.002100886751577491, + 0.0023667280438530724, + 0.0026158143016800747, + 0.0034943125079735177, + 0.005161623004823923, + 0.006949284041184, + 0.010531335494306404, + 0.013410061004833551 + ], + "conc_latency_avg_list": [ + 0.0018646425533687709, + 0.0020474016047948205, + 0.00225211486396273, + 0.00285420751423595, + 0.003711133378722069, + 0.004623532072278411, + 0.006552865012667485, + 0.008189225702132959 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 100, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 1.5 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 129.799, + "optimize_duration": 152.2479, + "load_duration": 282.0468, + "qps": 8596.7694, + "serial_latency_p99": 0.0024, + "serial_latency_p95": 0.0022, + "recall": 0.9416, + "ndcg": 0.9493, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 497.6866, + 2259.6612, + 4114.0036, + 6455.3584, + 7309.2383, + 7686.907, + 8294.3018, + 8596.7694 + ], + "conc_latency_p99_list": [ + 0.002440318200387992, + 0.0028159428038634342, + 0.0031061971996678038, + 0.004590758396079762, + 0.00719438069412717, + 0.009845319669257151, + 0.014379469840059753, + 0.018956097210466392 + ], + "conc_latency_p95_list": [ + 0.0022961719951126724, + 0.002580139000201598, + 0.0028258040038053878, + 0.003789236750890268, + 0.005791235491051338, + 0.00791677404558868, + 0.011584387852053624, + 0.015152870106976477 + ], + "conc_latency_avg_list": [ + 0.0020063849993894605, + 0.002208682703794502, + 0.002425080457737401, + 0.003086665132816786, + 0.00408523815973775, + 0.005170043147020008, + 0.007138938063080818, + 0.009125464665924427 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 100, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 2.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 129.799, + "optimize_duration": 152.2479, + "load_duration": 282.0468, + "qps": 7704.3625, + "serial_latency_p99": 0.0027, + "serial_latency_p95": 0.0025, + "recall": 0.9541, + "ndcg": 0.96, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 464.498, + 2087.0727, + 3811.3426, + 5897.8764, + 6613.5685, + 6995.184, + 7496.7496, + 7704.3625 + ], + "conc_latency_p99_list": [ + 0.002632544774387497, + 0.0030649999552406367, + 0.003373088193475267, + 0.004990858241653769, + 0.00786220946611138, + 0.010790031323267592, + 0.015690509527339604, + 0.020764024818781757 + ], + "conc_latency_p95_list": [ + 0.002470412495313212, + 0.002798333394457586, + 0.0030606225445808377, + 0.004139978906459873, + 0.006436757100163957, + 0.008721163390146102, + 0.012695113198424209, + 0.016752441306016403 + ], + "conc_latency_avg_list": [ + 0.002149911288992391, + 0.0023915170520859654, + 0.002617775435786274, + 0.0033781344953593517, + 0.0045130488491076, + 0.005680457334630225, + 0.007906434767685636, + 0.010188877722018175 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 100, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 2.5 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 129.799, + "optimize_duration": 152.2479, + "load_duration": 282.0468, + "qps": 7023.6735, + "serial_latency_p99": 0.003, + "serial_latency_p95": 0.0028, + "recall": 0.962, + "ndcg": 0.9667, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 437.0253, + 1938.4624, + 3454.7847, + 5460.2663, + 6058.6283, + 6398.7693, + 6828.1471, + 7023.6735 + ], + "conc_latency_p99_list": [ + 0.0028729987128463105, + 0.0033484641878749246, + 0.0037352688424289217, + 0.0053953273908700725, + 0.008702208310278365, + 0.011712039967096652, + 0.0172460880043218, + 0.02189830483126571 + ], + "conc_latency_p95_list": [ + 0.00266674381273333, + 0.003031579001981299, + 0.0033792859961977225, + 0.004472345393151045, + 0.007129233997693518, + 0.009540854604711059, + 0.014027368000824936, + 0.018085699503717478 + ], + "conc_latency_avg_list": [ + 0.0022852531678980575, + 0.0025748727575029204, + 0.0028880249144625355, + 0.003650684004208795, + 0.004931203325481902, + 0.006211804309036586, + 0.008678531268037587, + 0.011200709193523 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 100, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 3.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 129.799, + "optimize_duration": 152.2479, + "load_duration": 282.0468, + "qps": 6031.3725, + "serial_latency_p99": 0.0033, + "serial_latency_p95": 0.003, + "recall": 0.971, + "ndcg": 0.9743, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 375.0077, + 1749.9239, + 3171.188, + 4757.8121, + 5337.8052, + 5585.8874, + 5830.76, + 6031.3725 + ], + "conc_latency_p99_list": [ + 0.0034118053846759725, + 0.00371807467265171, + 0.004087487124343162, + 0.006445972005603836, + 0.0099661920015933, + 0.013419415393291265, + 0.01954109726633761, + 0.02450512952229475 + ], + "conc_latency_p95_list": [ + 0.0031440883423783815, + 0.0033821857992734294, + 0.003687061999517027, + 0.0053249524033162745, + 0.008198342995456187, + 0.011055400453187756, + 0.01616062365719699, + 0.020329035600298084 + ], + "conc_latency_avg_list": [ + 0.002663293590200931, + 0.0028528372994040497, + 0.0031468442372841717, + 0.004191480480374251, + 0.005595629037850519, + 0.007119491187741896, + 0.010171118781282724, + 0.013044592076065837 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 100, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 4.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 129.799, + "optimize_duration": 152.2479, + "load_duration": 282.0468, + "qps": 5258.1868, + "serial_latency_p99": 0.0036, + "serial_latency_p95": 0.0033, + "recall": 0.9768, + "ndcg": 0.9793, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 351.2889, + 1555.9976, + 2891.7532, + 4328.3041, + 4710.0472, + 4944.6567, + 5159.0143, + 5258.1868 + ], + "conc_latency_p99_list": [ + 0.0036150889145210364, + 0.004169191400287673, + 0.004474782356992364, + 0.00714936985692475, + 0.011309100479702464, + 0.014964991490269298, + 0.02106605595399745, + 0.02670282432547537 + ], + "conc_latency_p95_list": [ + 0.0032958149939076972, + 0.0038005771988537163, + 0.004026532001444138, + 0.0059051988064311445, + 0.0093500265997136, + 0.012421341851586474, + 0.017624663742026314, + 0.022111607256374555 + ], + "conc_latency_avg_list": [ + 0.0028433795541688497, + 0.003208622344277306, + 0.0034516157786571304, + 0.004607007276075149, + 0.006345752606166889, + 0.008043607402151223, + 0.01149538342406618, + 0.014941377751891149 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq4u-fp16-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 100, + "sq_type": "SQ4U", + "refine": true, + "refine_type": "FP16", + "refine_k": 5.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 119.5399, + "optimize_duration": 235.8939, + "load_duration": 355.4338, + "qps": 5973.0024, + "serial_latency_p99": 0.0024, + "serial_latency_p95": 0.0023, + "recall": 0.9192, + "ndcg": 0.9299, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 192.2873, + 780.772, + 1243.1343, + 2209.7863, + 5135.3255, + 5268.6684, + 5595.5025, + 5973.0024 + ], + "conc_latency_p99_list": [ + 0.006922555523342447, + 0.011980964867980226, + 0.01822498522611567, + 0.021657125554483937, + 0.011112995611620146, + 0.015273953418945892, + 0.021470034882659094, + 0.025694710013340227 + ], + "conc_latency_p95_list": [ + 0.006153562400140799, + 0.009661811696423684, + 0.013727128539176193, + 0.01456564510299358, + 0.00905536999925971, + 0.012242838197562377, + 0.017436827097844797, + 0.020891863998258486 + ], + "conc_latency_avg_list": [ + 0.005195547580597013, + 0.00639603304659674, + 0.0080334040769494, + 0.009027100221592798, + 0.0058122537237125, + 0.007549590091448817, + 0.010597991937403054, + 0.01314884199753361 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 100, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 119.5399, + "optimize_duration": 235.8939, + "load_duration": 355.4338, + "qps": 5416.5758, + "serial_latency_p99": 0.0026, + "serial_latency_p95": 0.0024, + "recall": 0.9334, + "ndcg": 0.9421, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 468.5468, + 2023.0586, + 3273.7331, + 4338.5873, + 4722.177, + 4899.851, + 5215.5063, + 5416.5758 + ], + "conc_latency_p99_list": [ + 0.002587841608328745, + 0.0031619464718096405, + 0.003964070154324872, + 0.007774805837543681, + 0.012349921874993024, + 0.01622475358992233, + 0.022378725241142112, + 0.02744747619624828 + ], + "conc_latency_p95_list": [ + 0.0024390827456954867, + 0.0029180134042690042, + 0.0035825525046675466, + 0.006318293401272964, + 0.009966622248612111, + 0.013235174745204858, + 0.018063901497225743, + 0.02230343740739044 + ], + "conc_latency_avg_list": [ + 0.0021313683553780983, + 0.0024673246161570224, + 0.0030484556683506147, + 0.0045937434301476935, + 0.006330211445594135, + 0.008107756352776998, + 0.01136996968107513, + 0.014491805104902885 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 120, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 119.5399, + "optimize_duration": 235.8939, + "load_duration": 355.4338, + "qps": 4771.4324, + "serial_latency_p99": 0.0028, + "serial_latency_p95": 0.0025, + "recall": 0.9479, + "ndcg": 0.9545, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 446.4305, + 1857.0762, + 3066.2065, + 3959.9145, + 4119.6984, + 4404.2396, + 4654.3656, + 4771.4324 + ], + "conc_latency_p99_list": [ + 0.0027465902952826583, + 0.003445194798405282, + 0.004290146040148099, + 0.008809843383787666, + 0.014246009238704564, + 0.017628938370035024, + 0.024053513460094107, + 0.029474502794328145 + ], + "conc_latency_p95_list": [ + 0.0025784781464608377, + 0.0031829175946768372, + 0.003835800604429096, + 0.0071477785022580065, + 0.011466846610710487, + 0.014443101210054009, + 0.019454453799698967, + 0.023967151201213708 + ], + "conc_latency_avg_list": [ + 0.002237092280444995, + 0.0026882400384546624, + 0.0032547429679731454, + 0.005034559716886033, + 0.007252702527297999, + 0.009024968635487724, + 0.012743531884434339, + 0.016486166204689928 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 150, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 119.5399, + "optimize_duration": 235.8939, + "load_duration": 355.4338, + "qps": 4006.3994, + "serial_latency_p99": 0.0032, + "serial_latency_p95": 0.003, + "recall": 0.9609, + "ndcg": 0.966, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 396.0794, + 1682.937, + 2770.1408, + 3415.7329, + 3586.5656, + 3732.9793, + 3908.8395, + 4006.3994 + ], + "conc_latency_p99_list": [ + 0.0031631914011086342, + 0.003759940078307404, + 0.005007598159427288, + 0.010577416090527546, + 0.01645218172343448, + 0.0200205380024272, + 0.02614469664360513, + 0.03247302199597469 + ], + "conc_latency_p95_list": [ + 0.0029416235047392547, + 0.0034802541093085894, + 0.004256061747582862, + 0.008588075407897121, + 0.01299767559976317, + 0.016106622002553195, + 0.021291244098392777, + 0.026077511996845715 + ], + "conc_latency_avg_list": [ + 0.002521469348982409, + 0.0029665274119998614, + 0.0036030252400985627, + 0.005839950739688844, + 0.008331452198117406, + 0.010640699049339198, + 0.015161825352942679, + 0.019627049620518894 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 200, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 119.5399, + "optimize_duration": 235.8939, + "load_duration": 355.4338, + "qps": 3441.7597, + "serial_latency_p99": 0.0035, + "serial_latency_p95": 0.0032, + "recall": 0.9682, + "ndcg": 0.9725, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 363.4573, + 1530.4211, + 2519.3499, + 2941.0933, + 3130.5066, + 3239.8074, + 3357.9662, + 3441.7597 + ], + "conc_latency_p99_list": [ + 0.003447128901898394, + 0.004104390731372401, + 0.005795499211671998, + 0.012608604685810857, + 0.018390358952165124, + 0.02161771130544366, + 0.029535210086614822, + 0.03676844512228854 + ], + "conc_latency_p95_list": [ + 0.0032040981095633465, + 0.0037800333026098087, + 0.004730919548455857, + 0.010123601651866907, + 0.014520535804331302, + 0.017753759350307517, + 0.023507353749300814, + 0.029112758993869645 + ], + "conc_latency_avg_list": [ + 0.0027478315701259456, + 0.0032620836103758183, + 0.003962182206516774, + 0.006784177567489496, + 0.009538412010322416, + 0.012277581280440339, + 0.017678232976666337, + 0.022830987265851796 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 250, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 119.5399, + "optimize_duration": 235.8939, + "load_duration": 355.4338, + "qps": 3040.6216, + "serial_latency_p99": 0.0037, + "serial_latency_p95": 0.0035, + "recall": 0.9734, + "ndcg": 0.9771, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 323.1632, + 1408.5716, + 2291.0458, + 2658.8647, + 2809.4563, + 2889.303, + 2958.6865, + 3040.6216 + ], + "conc_latency_p99_list": [ + 0.0038972479960648343, + 0.004427719511440956, + 0.006464274920726896, + 0.013835774816980114, + 0.01854686296428553, + 0.023528500349348183, + 0.030925163915235258, + 0.037629884978523494 + ], + "conc_latency_p95_list": [ + 0.0036286949907662347, + 0.00409807980468031, + 0.005293481396074639, + 0.01114347539114533, + 0.015239884803304439, + 0.01894600450323196, + 0.02535856414833688, + 0.03149616299779154 + ], + "conc_latency_avg_list": [ + 0.0030909596088423805, + 0.0035447166881325863, + 0.004357504468202672, + 0.007503025706852371, + 0.010637931999990373, + 0.013780835663880903, + 0.02002760247152969, + 0.02587108250217688 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 300, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 119.5399, + "optimize_duration": 235.8939, + "load_duration": 355.4338, + "qps": 2446.7373, + "serial_latency_p99": 0.0043, + "serial_latency_p95": 0.004, + "recall": 0.9791, + "ndcg": 0.9822, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 290.4004, + 1199.0654, + 1943.7472, + 2207.5563, + 2272.2082, + 2361.8647, + 2425.272, + 2446.7373 + ], + "conc_latency_p99_list": [ + 0.004225126459205057, + 0.005225188090844314, + 0.007753408416756429, + 0.015723756006627808, + 0.021543593837413895, + 0.025226465429732312, + 0.0348595633450895, + 0.04409603113395864 + ], + "conc_latency_p95_list": [ + 0.003936908097239211, + 0.004869158701330889, + 0.006411225302144882, + 0.012806271501176525, + 0.017710478595108724, + 0.02138321524907951, + 0.02917539790214505, + 0.037459137551195454 + ], + "conc_latency_avg_list": [ + 0.003439496089142086, + 0.004164736080481502, + 0.005136606676356694, + 0.0090353538125237, + 0.013157439387184814, + 0.01685513374865052, + 0.024467911853133933, + 0.03215223014404708 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 400, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 119.5399, + "optimize_duration": 235.8939, + "load_duration": 355.4338, + "qps": 2084.6245, + "serial_latency_p99": 0.005, + "serial_latency_p95": 0.0046, + "recall": 0.9819, + "ndcg": 0.9847, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 257.7354, + 1074.5131, + 1699.1152, + 1906.3127, + 1964.552, + 1992.5973, + 2059.3939, + 2084.6245 + ], + "conc_latency_p99_list": [ + 0.004679669999168255, + 0.0057560302416095515, + 0.008956946645048449, + 0.016920548141788453, + 0.022776664053235435, + 0.02843890285366797, + 0.039300132958451285, + 0.0494756092831085 + ], + "conc_latency_p95_list": [ + 0.004424713403568603, + 0.005422145003103651, + 0.007496941405406687, + 0.014108828103053384, + 0.019311829509388187, + 0.024556438496802002, + 0.033556375399348325, + 0.04314066854931297 + ], + "conc_latency_avg_list": [ + 0.00387603491228106, + 0.004647482651951955, + 0.005875930555797928, + 0.010467915104743828, + 0.015220352811585824, + 0.019988946757462604, + 0.028783135202314097, + 0.037787432564769345 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "Milvus", + "db_config": { + "db_label": "16c64g-sq8-force_merge", + "version": "2.6.14", + "note": "", + "uri": "**********", + "user": null, + "password": null, + "num_shards": 1, + "replica_number": 1 + }, + "db_case_config": { + "index": "HNSW_SQ", + "metric_type": "COSINE", + "use_partition_key": false, + "M": 16, + "efConstruction": 300, + "ef": 500, + "sq_type": "SQ8", + "refine": false, + "refine_type": "FP32", + "refine_k": 1.0 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ], + "load_concurrency": 0 + }, + "label": ":)" + } + ] +} \ No newline at end of file diff --git a/vectordb_bench/results/TurboPuffer/result_20260331_standard_2025_turbopuffer.json b/vectordb_bench/results/TurboPuffer/result_20260331_standard_2025_turbopuffer.json new file mode 100644 index 000000000..3f6294f67 --- /dev/null +++ b/vectordb_bench/results/TurboPuffer/result_20260331_standard_2025_turbopuffer.json @@ -0,0 +1,3072 @@ +{ + "run_id": "45faeeb9909d4c20982712629b7109f8", + "task_label": "standard_2025", + "results": [ + { + "metrics": { + "max_load_count": 0, + "insert_duration": 3636.6248, + "optimize_duration": 60.1176, + "load_duration": 3696.7424, + "qps": 649.8781, + "serial_latency_p99": 0.0552, + "serial_latency_p95": 0.0323, + "recall": 0.8352, + "ndcg": 0.8489, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 5.0181, + 136.3428, + 401.9281, + 631.8033, + 649.8781, + 644.0239, + 638.2568, + 619.0102 + ], + "conc_latency_p99_list": [ + 0.7842317419981555, + 0.1055335910506619, + 0.04784800433084457, + 0.05269464588025578, + 0.1962103870000271, + 1.0594186752803216, + 1.0895923817619768, + 1.105607972859143 + ], + "conc_latency_p95_list": [ + 0.4166556664986274, + 0.06923620555135131, + 0.03216669879984694, + 0.04018809000044712, + 0.07153936980175785, + 0.08024858280041376, + 0.13536313344884537, + 1.0559816184473676 + ], + "conc_latency_avg_list": [ + 0.19879915163556802, + 0.03653489135292955, + 0.024727361365498506, + 0.031303927102332416, + 0.04542742658123844, + 0.06072973767027509, + 0.0906762097364659, + 0.12303716134739207 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test_10m" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 351.7114, + "serial_latency_p99": 0.0467, + "serial_latency_p95": 0.0306, + "recall": 0.8735, + "ndcg": 0.8847, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 39.0694, + 207.8572, + 315.6016, + 351.7114, + 350.2248, + 341.017, + 342.4504, + 333.8541 + ], + "conc_latency_p99_list": [ + 0.04308568556152748, + 0.03936693226052748, + 0.04930899777031294, + 0.08584493122052665, + 1.0702309198401785, + 1.1136302413583326, + 1.2335058578685432, + 2.3380776378013257 + ], + "conc_latency_p95_list": [ + 0.02841167169972323, + 0.02715096870197158, + 0.03799563039938221, + 0.07426544004883909, + 0.12973272005019681, + 0.21250388589869676, + 1.0903613543001485, + 1.1055554908001795 + ], + "conc_latency_avg_list": [ + 0.025532494878335706, + 0.02396293916721007, + 0.03149805626143546, + 0.056246979911343725, + 0.08423275960825198, + 0.11371272981539232, + 0.16889921984869344, + 0.22411732816279095 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test_10m" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Large Cohere (768dim, 10M)", + "filter_rate": 0.5 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 365.2505, + "serial_latency_p99": 0.0349, + "serial_latency_p95": 0.0247, + "recall": 0.8251, + "ndcg": 0.8424, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 41.7453, + 217.4248, + 327.3086, + 365.2505, + 360.5899, + 361.9818, + 353.6243, + 351.7834 + ], + "conc_latency_p99_list": [ + 0.03651399044829308, + 0.03795003006755009, + 0.04851697580088516, + 0.0817924941409364, + 1.069550116062965, + 1.1113606851896825, + 1.2188607804002098, + 2.3407695170007345 + ], + "conc_latency_p95_list": [ + 0.025673050000477815, + 0.025719283201397047, + 0.0362045420006325, + 0.07120951019969653, + 0.12899555909789345, + 0.17483308564860484, + 1.085536041801606, + 1.1010552038016612 + ], + "conc_latency_avg_list": [ + 0.023894811787483095, + 0.02290646905055157, + 0.03037167207873524, + 0.05417950100621118, + 0.08193541962097284, + 0.1079860384930871, + 0.1626031282242101, + 0.21517478888871755 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test_10m" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Large Cohere (768dim, 10M)", + "filter_rate": 0.8 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 369.4921, + "serial_latency_p99": 0.0416, + "serial_latency_p95": 0.026, + "recall": 0.779, + "ndcg": 0.8011, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 44.1762, + 217.1941, + 333.4918, + 368.3695, + 369.4921, + 361.0564, + 358.8268, + 351.6052 + ], + "conc_latency_p99_list": [ + 0.037339031600567986, + 0.04319219880198944, + 0.04170634405851157, + 0.08140748869991507, + 1.075197636150333, + 1.113729548148076, + 1.185450099499576, + 2.3347086974994453 + ], + "conc_latency_p95_list": [ + 0.025345681600447277, + 0.026001171001553303, + 0.035399802850224656, + 0.0708943822002766, + 0.11817662325156564, + 0.16750261040106082, + 1.0850413800017122, + 1.1004046580001159 + ], + "conc_latency_avg_list": [ + 0.022580311149034315, + 0.022936271871885103, + 0.02982460058318166, + 0.05370093295877385, + 0.07984126562141454, + 0.10845894286435549, + 0.16060839830850934, + 0.21328552188517172 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test_10m" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Large Cohere (768dim, 10M)", + "filter_rate": 0.9 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 370.7241, + "serial_latency_p99": 0.0496, + "serial_latency_p95": 0.0256, + "recall": 0.7177, + "ndcg": 0.7469, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 42.3641, + 213.6356, + 337.7677, + 367.4005, + 370.7241, + 352.4176, + 344.8198, + 354.0423 + ], + "conc_latency_p99_list": [ + 0.04112669600999651, + 0.04059317171937442, + 0.049354484229988935, + 0.08914589621916108, + 1.0625034953784052, + 1.1084485840195337, + 1.1761026821609268, + 2.329700628177888 + ], + "conc_latency_p95_list": [ + 0.026073782299499724, + 0.026734366401069565, + 0.03505219174985541, + 0.07247991124786494, + 0.11844929140133904, + 0.19264427510115556, + 1.0887599481509824, + 1.1004490541015912 + ], + "conc_latency_avg_list": [ + 0.023546151951321655, + 0.02331067781469216, + 0.02942534475558775, + 0.05384849155748122, + 0.07935110206611184, + 0.10908346295012428, + 0.16543306380794237, + 0.21503563146286667 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test_10m" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Large Cohere (768dim, 10M)", + "filter_rate": 0.95 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 382.5332, + "serial_latency_p99": 0.0547, + "serial_latency_p95": 0.0261, + "recall": 0.6135, + "ndcg": 0.6532, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 42.6797, + 211.9221, + 340.0303, + 381.3494, + 382.5332, + 377.5534, + 372.3789, + 370.273 + ], + "conc_latency_p99_list": [ + 0.045958382528879155, + 0.04791345044010085, + 0.050254700950063146, + 0.08182212736079236, + 1.0455512913525304, + 1.1066815286008933, + 1.1499066802006566, + 2.330022009398708 + ], + "conc_latency_p95_list": [ + 0.027506814953085268, + 0.030191246797767208, + 0.035157976998561935, + 0.06934378335081418, + 0.1208363270012342, + 0.17324353699950734, + 1.0816264840002987, + 1.0978374554006223 + ], + "conc_latency_avg_list": [ + 0.023371494825615652, + 0.023497298190272724, + 0.02923274826642055, + 0.051893872435525415, + 0.07698122536272771, + 0.1032508273402585, + 0.15527307758066816, + 0.20625486973445892 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test_10m" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Large Cohere (768dim, 10M)", + "filter_rate": 0.98 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 81.8678, + "serial_latency_p99": 0.1056, + "serial_latency_p95": 0.1021, + "recall": 0.8751, + "ndcg": 0.8923, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 10.0132, + 45.2108, + 69.7393, + 81.8678, + 81.5054, + 79.5748, + 80.1286, + 76.405 + ], + "conc_latency_p99_list": [ + 0.11135667605140044, + 0.12466917503959847, + 0.17545606489948118, + 0.4186298333996091, + 1.4131757669402578, + 2.561454358698395, + 4.161437169238486, + 6.53146800307746 + ], + "conc_latency_p95_list": [ + 0.10567549315092037, + 0.11936616160019184, + 0.15836084999864397, + 0.3964126334994944, + 1.2486386469990973, + 1.4115760019994923, + 2.527067465399887, + 2.6629810965998333 + ], + "conc_latency_avg_list": [ + 0.09962701216235118, + 0.11013113512517288, + 0.14239347650973677, + 0.24165083264509568, + 0.36060090070201456, + 0.4901049624103434, + 0.720209461182002, + 0.9583824911724366 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test_10m" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Large Cohere (768dim, 10M)", + "filter_rate": 0.99 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 91.8612, + "serial_latency_p99": 0.0857, + "serial_latency_p95": 0.0809, + "recall": 0.8799, + "ndcg": 0.8975, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 12.999, + 57.8487, + 82.2415, + 91.8612, + 90.3749, + 90.0362, + 89.1634, + 87.8603 + ], + "conc_latency_p99_list": [ + 0.08638456059743475, + 0.09575280532066244, + 0.13928119900861935, + 0.4007615396678755, + 1.3269691034200513, + 2.511851395880221, + 4.069974964441136, + 4.318752965519816 + ], + "conc_latency_p95_list": [ + 0.08070138149923878, + 0.09151377140078694, + 0.13317929814766102, + 0.33960816225135204, + 1.222629544099982, + 1.2648818186004065, + 2.463837313051044, + 2.5308212226009346 + ], + "conc_latency_avg_list": [ + 0.07674188703336754, + 0.08605192841317442, + 0.12095865782566981, + 0.2152256534213578, + 0.3258099637445097, + 0.4338084209719558, + 0.6348769175872554, + 0.8261389732993693 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test_10m" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Large Cohere (768dim, 10M)", + "filter_rate": 0.995 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 96.592, + "serial_latency_p99": 0.0769, + "serial_latency_p95": 0.0671, + "recall": 0.9178, + "ndcg": 0.9309, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 15.4173, + 68.7506, + 91.1578, + 95.9584, + 96.592, + 96.5377, + 92.9689, + 92.3363 + ], + "conc_latency_p99_list": [ + 0.07026230202154693, + 0.09216422941106428, + 0.1530633716819285, + 0.37221308532047254, + 1.379136629800488, + 2.4759424806782047, + 4.150151355230372, + 4.311918809250528 + ], + "conc_latency_p95_list": [ + 0.06769757184974878, + 0.07770936130109475, + 0.1223220942003536, + 0.3146231973501927, + 1.2210356577998025, + 1.2695832258003064, + 2.432397950649647, + 2.5293083527494673 + ], + "conc_latency_avg_list": [ + 0.06470390199121445, + 0.072410190861118, + 0.10899080468085105, + 0.20620261645924234, + 0.3044200164964746, + 0.40215828176873775, + 0.5961679787750713, + 0.7680528748023352 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test_10m" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Large Cohere (768dim, 10M)", + "filter_rate": 0.998 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 100.0554, + "serial_latency_p99": 0.0693, + "serial_latency_p95": 0.0649, + "recall": 0.9638, + "ndcg": 0.9702, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 16.4825, + 73.9278, + 94.48, + 100.0554, + 99.5101, + 96.945, + 94.0424, + 86.2962 + ], + "conc_latency_p99_list": [ + 0.06726060309974854, + 0.08376507117056463, + 0.12645121190198552, + 0.39609605101897616, + 1.3409943886408304, + 2.4752014657589463, + 4.116221232489005, + 4.324473266399982 + ], + "conc_latency_p95_list": [ + 0.06371058149852615, + 0.07187465289953253, + 0.11859881825057528, + 0.2999235360995954, + 1.2061096817997168, + 1.2548316742995667, + 2.412594861300204, + 2.5453477221013596 + ], + "conc_latency_avg_list": [ + 0.06052078211502204, + 0.06738729749599892, + 0.10525026012149993, + 0.19776199416695284, + 0.2947393509041799, + 0.39903049677303315, + 0.5802193109362205, + 0.8205917814459879 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test_10m" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Large Cohere (768dim, 10M)", + "filter_rate": 0.999 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 237.1288, + "optimize_duration": 60.1231, + "load_duration": 297.2519, + "qps": 798.328, + "serial_latency_p99": 0.0567, + "serial_latency_p95": 0.0373, + "recall": 0.8993, + "ndcg": 0.909, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 7.2334, + 233.3539, + 418.0091, + 779.6834, + 798.328, + 721.0519, + 665.1265, + 584.2167 + ], + "conc_latency_p99_list": [ + 0.16710825715950703, + 0.06532752437056843, + 0.06613579651964772, + 0.06110905407032987, + 0.14510449524073932, + 1.0521202659806477, + 1.0834973058097968, + 1.1199298221199219 + ], + "conc_latency_p95_list": [ + 0.1503542814499269, + 0.04019359780040751, + 0.03864274919997114, + 0.03302840365022349, + 0.06032294020042168, + 0.07371430989933284, + 0.12393024720067815, + 1.0562808335987939 + ], + "conc_latency_avg_list": [ + 0.13791148913299642, + 0.021343171084931285, + 0.023774816974899124, + 0.025365002319208883, + 0.03695695700041601, + 0.05426009768172259, + 0.08686200089993441, + 0.13066586199842864 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 802.6923, + "serial_latency_p99": 0.0481, + "serial_latency_p95": 0.0323, + "recall": 0.935, + "ndcg": 0.9417, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 35.1162, + 166.394, + 441.2273, + 754.7672, + 802.6923, + 758.6779, + 709.8788, + 667.6315 + ], + "conc_latency_p99_list": [ + 0.05474694928023395, + 0.05710166565986581, + 0.08122192963965079, + 0.053906371219891225, + 0.1059555222807467, + 1.0329836665192853, + 1.0822717395997459, + 1.1019392383902227 + ], + "conc_latency_p95_list": [ + 0.033523448400228514, + 0.03988007639854914, + 0.03580541750015981, + 0.03533476559914561, + 0.05827280570065341, + 0.07033409700015901, + 0.10542240839895384, + 1.0461306155005332 + ], + "conc_latency_avg_list": [ + 0.028406082829701498, + 0.0299218017148686, + 0.02252275923560204, + 0.02620027550993044, + 0.03675704353476552, + 0.051493407375862076, + 0.0814369894123999, + 0.114157400844387 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Medium Cohere (768dim, 1M)", + "filter_rate": 0.5 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 310.957, + "serial_latency_p99": 0.0494, + "serial_latency_p95": 0.0478, + "recall": 0.9698, + "ndcg": 0.9735, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 22.9937, + 114.2135, + 201.3758, + 310.957, + 284.36, + 278.919, + 275.2043, + 268.6154 + ], + "conc_latency_p99_list": [ + 0.05187608798047224, + 0.0676384811005301, + 0.061836029370570034, + 0.10488168669999139, + 1.0839724454297168, + 1.1247000296189251, + 1.1835066912906216, + 2.350891308639548 + ], + "conc_latency_p95_list": [ + 0.04523930709929118, + 0.046784667498923224, + 0.05690364935007892, + 0.09261150339998493, + 0.13370714204984316, + 0.30792148500040484, + 1.109438732399667, + 1.1224438496010407 + ], + "conc_latency_avg_list": [ + 0.04338349602020482, + 0.04359157635044708, + 0.04934177400048448, + 0.06363149687529962, + 0.10389666149204274, + 0.13943306853059848, + 0.2093151750419116, + 0.28126611132382795 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Medium Cohere (768dim, 1M)", + "filter_rate": 0.8 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 260.4031, + "serial_latency_p99": 0.0483, + "serial_latency_p95": 0.0461, + "recall": 0.9828, + "ndcg": 0.9852, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 23.2538, + 105.0145, + 165.942, + 260.4031, + 255.1092, + 249.9039, + 247.1553, + 222.8946 + ], + "conc_latency_p99_list": [ + 0.04928288878063535, + 0.05744525966034426, + 0.08332391123971328, + 0.12499408589974335, + 1.1001869340804298, + 1.1539904669602765, + 2.271939874058515, + 2.3963180435212417 + ], + "conc_latency_p95_list": [ + 0.04398724759921606, + 0.05287559080006758, + 0.06577553119968797, + 0.11041119500077912, + 0.1910506329497366, + 1.079135947499708, + 1.123120172900053, + 1.156959662200461 + ], + "conc_latency_avg_list": [ + 0.042898485931390104, + 0.0473868923309176, + 0.059896585720115844, + 0.07594923477056366, + 0.11576974236313346, + 0.15613794048676635, + 0.23384194864694827, + 0.34093762910663383 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Medium Cohere (768dim, 1M)", + "filter_rate": 0.9 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 206.0934, + "serial_latency_p99": 0.0567, + "serial_latency_p95": 0.0493, + "recall": 0.9795, + "ndcg": 0.9827, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 23.9853, + 111.9502, + 167.2983, + 206.0934, + 202.6785, + 200.7624, + 198.7852, + 176.9053 + ], + "conc_latency_p99_list": [ + 0.04505532840961678, + 0.0549183023203659, + 0.07183914795945383, + 0.16050115711950638, + 1.1392679925593985, + 1.1783225936005692, + 2.391149005459738, + 2.4627247357385564 + ], + "conc_latency_p95_list": [ + 0.04352937004987325, + 0.049765212599595536, + 0.0665941942007521, + 0.14062045170048804, + 0.25411106819992707, + 1.1207731039994542, + 1.1548708326008637, + 1.2187981356008997 + ], + "conc_latency_avg_list": [ + 0.04158898759147668, + 0.044488593964723565, + 0.05943009694892961, + 0.09602951410439428, + 0.145682298229123, + 0.19324255140426388, + 0.2893373419111252, + 0.4256243749569817 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Medium Cohere (768dim, 1M)", + "filter_rate": 0.95 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 184.5363, + "serial_latency_p99": 0.0534, + "serial_latency_p95": 0.0447, + "recall": 0.9681, + "ndcg": 0.9731, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 23.3311, + 105.7534, + 154.8527, + 184.5363, + 179.2525, + 179.9984, + 173.6043, + 172.2228 + ], + "conc_latency_p99_list": [ + 0.06133968915926745, + 0.05785074116065515, + 0.07778257604990356, + 0.1836007817494645, + 1.1589217726791685, + 1.2315237454791341, + 2.4227708060203077, + 2.5051683164208227 + ], + "conc_latency_p95_list": [ + 0.044938461699348405, + 0.053342464800516604, + 0.07181120149953131, + 0.1550450290005756, + 0.37187840719961957, + 1.1339543323992984, + 1.174229825800103, + 1.2181544718499933 + ], + "conc_latency_avg_list": [ + 0.04275554558972205, + 0.047119999140656205, + 0.06418707822424495, + 0.1072447022017609, + 0.1639824330546471, + 0.21581640828899204, + 0.3248775175758776, + 0.4320007027329427 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Medium Cohere (768dim, 1M)", + "filter_rate": 0.98 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 346.5847, + "serial_latency_p99": 0.0427, + "serial_latency_p95": 0.0306, + "recall": 0.9631, + "ndcg": 0.9691, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 24.832, + 105.4399, + 268.1794, + 342.1315, + 346.5847, + 339.9961, + 331.4736, + 318.4889 + ], + "conc_latency_p99_list": [ + 0.0524532141194868, + 0.124523086280969, + 0.05130710799858207, + 0.09639830237927527, + 0.6372997376802239, + 1.1167713518602072, + 1.1833148257201527, + 2.346942362739901 + ], + "conc_latency_p95_list": [ + 0.04208997349924175, + 0.054497509400971464, + 0.04311577799853694, + 0.0820058475001133, + 0.15190685399975346, + 0.24409308345066127, + 1.094288076799785, + 1.113402110500283 + ], + "conc_latency_avg_list": [ + 0.040171743147231896, + 0.04722931638843159, + 0.03705184992629306, + 0.05782180853289151, + 0.0850777291892508, + 0.11466982697445866, + 0.17464619983809074, + 0.23867489188873692 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Medium Cohere (768dim, 1M)", + "filter_rate": 0.99 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 284.6367, + "serial_latency_p99": 0.0476, + "serial_latency_p95": 0.0343, + "recall": 0.9788, + "ndcg": 0.9824, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 33.6033, + 154.3359, + 234.9368, + 284.6367, + 281.0354, + 282.1947, + 275.5716, + 254.759 + ], + "conc_latency_p99_list": [ + 0.04858522409849683, + 0.048488989419420224, + 0.05480865936007828, + 0.11453194788005944, + 1.0936976923593464, + 1.1293674966106844, + 2.3225755058392124, + 2.393918312400474 + ], + "conc_latency_p95_list": [ + 0.03065781050008809, + 0.037750776849679826, + 0.04890473074956389, + 0.09784386339888443, + 0.1834858106001775, + 0.5145842184007118, + 1.1164754623508997, + 1.13096353059982 + ], + "conc_latency_avg_list": [ + 0.02968536912167731, + 0.03225200436006818, + 0.04233185695698942, + 0.06951284309111169, + 0.10508535649574902, + 0.1382588987117255, + 0.2097049643681289, + 0.2930996819200318 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Medium Cohere (768dim, 1M)", + "filter_rate": 0.995 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 323.0238, + "serial_latency_p99": 0.0504, + "serial_latency_p95": 0.0276, + "recall": 1.0, + "ndcg": 1.0, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 33.45, + 152.7353, + 227.8618, + 275.5285, + 291.2986, + 323.0238, + 318.2655, + 312.4427 + ], + "conc_latency_p99_list": [ + 0.04559309834949112, + 0.048093286539624296, + 0.05670377755039221, + 0.11922176679041513, + 1.0838092030408004, + 1.1185716670006514, + 1.246932903139767, + 2.348052036049994 + ], + "conc_latency_p95_list": [ + 0.031013834000077622, + 0.03764021150072949, + 0.05072674379944146, + 0.10094266020023496, + 0.18840500040023464, + 0.29478117500002554, + 1.0974889842496849, + 1.115618919398821 + ], + "conc_latency_avg_list": [ + 0.029821064443336008, + 0.03260137427613773, + 0.0436201719452207, + 0.07183805259952356, + 0.10142021420372482, + 0.12089794632971683, + 0.18129370678602072, + 0.24532422969222006 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Medium Cohere (768dim, 1M)", + "filter_rate": 0.998 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 0.0, + "optimize_duration": 0.0, + "load_duration": 0.0, + "qps": 471.553, + "serial_latency_p99": 0.0441, + "serial_latency_p95": 0.0247, + "recall": 1.0, + "ndcg": 1.0, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 49.1528, + 228.9819, + 363.0592, + 468.8616, + 471.553, + 461.5481, + 437.5487, + 394.213 + ], + "conc_latency_p99_list": [ + 0.041978935619772575, + 0.04080429583897057, + 0.0448176010107636, + 0.08228810615008104, + 0.3177955880392612, + 1.090591485319801, + 1.134414788300455, + 2.3114677489898723 + ], + "conc_latency_p95_list": [ + 0.021382112400533514, + 0.02570402879937319, + 0.033227993899890854, + 0.06001253969943718, + 0.10880423700054961, + 0.13627656039943753, + 1.060789735999606, + 1.0911676228000942 + ], + "conc_latency_avg_list": [ + 0.020293962421219093, + 0.0217530582419813, + 0.02738142802709893, + 0.04220026652024836, + 0.06239387785636437, + 0.08461281376066486, + 0.13169568263657166, + 0.18839915684310343 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 400, + "custom_case": { + "dataset_with_size_type": "Medium Cohere (768dim, 1M)", + "filter_rate": 0.999 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 10000.231680193, + "optimize_duration": 60.23235460700016, + "load_duration": 0.0, + "qps": 0.0, + "serial_latency_p99": 0.0, + "serial_latency_p95": 0.0, + "recall": 0.0, + "ndcg": 0.0, + "conc_num_list": [], + "conc_qps_list": [], + "conc_latency_p99_list": [], + "conc_latency_p95_list": [], + "conc_latency_avg_list": [], + "st_ideal_insert_duration": 10000, + "st_search_stage_list": [ + 10, + 20, + 30, + 40, + 50, + 60, + 70, + 80, + 90, + 100, + 110 + ], + "st_search_time_list": [ + 1001.3061, + 2032.9136, + 3065.0184, + 4086.7507, + 5107.3667, + 6128.9501, + 7149.9124, + 8171.4762, + 9192.0596, + 10223.3342, + 10444.8913 + ], + "st_max_qps_list_list": [ + 362.5803, + 334.5913, + 235.4548, + 319.031, + 459.0171, + 305.2895, + 336.8119, + 317.4398, + 442.5824, + 830.8648, + 837.1841 + ], + "st_recall_list": [ + 0.1028, + 0.1942, + 0.2856, + 0.3713, + 0.4495, + 0.5356, + 0.6176, + 0.7044, + 0.7795, + 0.8358, + 0.8358 + ], + "st_ndcg_list": [ + 0.1033, + 0.1954, + 0.2882, + 0.3754, + 0.4551, + 0.5423, + 0.626, + 0.7145, + 0.7914, + 0.849, + 0.849 + ], + "st_serial_latency_p99_list": [ + 0.162, + 0.2745, + 0.1655, + 0.154, + 0.1724, + 0.2706, + 0.2654, + 0.1929, + 0.0724, + 0.033, + 0.0413 + ], + "st_serial_latency_p95_list": [ + 0.138, + 0.1042, + 0.1335, + 0.1357, + 0.0747, + 0.1695, + 0.1519, + 0.1432, + 0.0571, + 0.0211, + 0.0213 + ], + "st_conc_failed_rate_list": [ + 1.621796951021732e-05, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 7.329873632978568e-06, + 0.0, + 0.0 + ], + "st_conc_num_list_list": [ + [ + 20, + 30, + 40 + ], + [ + 20, + 30, + 40 + ], + [ + 20, + 30, + 40 + ], + [ + 20, + 30, + 40 + ], + [ + 20, + 30, + 40 + ], + [ + 20, + 30, + 40 + ], + [ + 20, + 30, + 40 + ], + [ + 20, + 30, + 40 + ], + [ + 20, + 30, + 40 + ], + [ + 20, + 30, + 40 + ], + [ + 20, + 30, + 40 + ] + ], + "st_conc_qps_list_list": [ + [ + 362.5803, + 305.9887, + 211.6508 + ], + [ + 313.8881, + 334.5913, + 295.2759 + ], + [ + 235.4548, + 187.7903, + 223.1515 + ], + [ + 195.555, + 255.0244, + 319.031 + ], + [ + 459.0171, + 448.8463, + 326.9188 + ], + [ + 264.3766, + 302.7591, + 305.2895 + ], + [ + 212.8641, + 336.8119, + 265.5836 + ], + [ + 164.1314, + 174.9407, + 317.4398 + ], + [ + 280.9929, + 234.8017, + 442.5824 + ], + [ + 793.081, + 830.8648, + 830.5245 + ], + [ + 790.8788, + 837.1841, + 831.3462 + ] + ], + "st_conc_latency_p99_list_list": [ + [ + 0.125567, + 1.124351, + 2.306047 + ], + [ + 0.140031, + 1.105919, + 1.235967 + ], + [ + 0.201983, + 1.233919, + 2.289663 + ], + [ + 0.270847, + 1.172479, + 1.171455 + ], + [ + 0.098623, + 1.064959, + 1.157119 + ], + [ + 0.156671, + 1.128447, + 1.165311 + ], + [ + 0.239615, + 1.102847, + 1.522687 + ], + [ + 0.300031, + 1.263615, + 1.216511 + ], + [ + 0.163583, + 1.207295, + 1.197055 + ], + [ + 0.042719, + 0.201983, + 1.076223 + ], + [ + 0.046463, + 0.231167, + 1.074175 + ] + ], + "st_conc_latency_p95_list_list": [ + [ + 0.094463, + 0.180351, + 1.168383 + ], + [ + 0.106879, + 0.162175, + 1.087487 + ], + [ + 0.143487, + 1.043455, + 1.123327 + ], + [ + 0.193151, + 0.275455, + 1.065983 + ], + [ + 0.068607, + 0.115583, + 1.075199 + ], + [ + 0.118655, + 0.174463, + 1.084415 + ], + [ + 0.162047, + 0.147711, + 1.101823 + ], + [ + 0.225023, + 1.132543, + 1.075199 + ], + [ + 0.119295, + 0.299263, + 0.238591 + ], + [ + 0.032479, + 0.062495, + 0.081855 + ], + [ + 0.034303, + 0.059647, + 0.075775 + ] + ], + "st_conc_latency_avg_list_list": [ + [ + 0.05500603695334308, + 0.09764594385089817, + 0.18784171186688073 + ], + [ + 0.06353041564585622, + 0.08927120173312625, + 0.13474179712740827 + ], + [ + 0.08473482217393206, + 0.15917699015615014, + 0.17835078322515305 + ], + [ + 0.10202393731369927, + 0.11714498760979741, + 0.12473291585827614 + ], + [ + 0.043456775709144496, + 0.06659470207128572, + 0.12171512893789603 + ], + [ + 0.07546223394734103, + 0.09867369148091878, + 0.1303126478911198 + ], + [ + 0.0937178086011191, + 0.0887245961053375, + 0.14989714770693796 + ], + [ + 0.12154873281339651, + 0.17085041091090097, + 0.12538872906231163 + ], + [ + 0.07100562943422423, + 0.1272563421099916, + 0.08991327977599742 + ], + [ + 0.024614513704250777, + 0.03488576620155038, + 0.04598832608029421 + ], + [ + 0.024699256453538636, + 0.03461592000922972, + 0.04602557062363449 + ] + ] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test_streaming_test3" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 200, + "custom_case": { + "dataset_with_size_type": "Large Cohere (768dim, 10M)", + "insert_rate": 1000, + "bulk_insert_ratio": 0.0 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 6900.174543077999, + "optimize_duration": 60.1439380869997, + "load_duration": 0.0, + "qps": 0.0, + "serial_latency_p99": 0.0, + "serial_latency_p95": 0.0, + "recall": 0.0, + "ndcg": 0.0, + "conc_num_list": [], + "conc_qps_list": [], + "conc_latency_p99_list": [], + "conc_latency_p95_list": [], + "conc_latency_avg_list": [], + "st_ideal_insert_duration": 20000, + "st_search_stage_list": [ + 90, + 100, + 110 + ], + "st_search_time_list": [ + 4901.1288, + 6922.6153, + 7155.3455 + ], + "st_max_qps_list_list": [ + 536.0198, + 1027.3522, + 1020.8349 + ], + "st_recall_list": [ + 0.7646, + 0.8365, + 0.836 + ], + "st_ndcg_list": [ + 0.7768, + 0.8503, + 0.8497 + ], + "st_serial_latency_p99_list": [ + 0.3132, + 0.0532, + 0.0406 + ], + "st_serial_latency_p95_list": [ + 0.2025, + 0.0385, + 0.0219 + ], + "st_conc_failed_rate_list": [ + 0.0, + 0.0, + 0.0 + ], + "st_conc_num_list_list": [ + [ + 20, + 30, + 40 + ], + [ + 20, + 30, + 40 + ], + [ + 20, + 30, + 40 + ] + ], + "st_conc_qps_list_list": [ + [ + 404.5543, + 429.4512, + 536.0198 + ], + [ + 944.4992, + 1025.9923, + 1027.3522 + ], + [ + 930.6361, + 1020.5733, + 1020.8349 + ] + ], + "st_conc_latency_p99_list_list": [ + [ + 0.139007, + 1.069055, + 1.088511 + ], + [ + 0.038335, + 0.077695, + 0.569855 + ], + [ + 0.042943, + 0.078015, + 0.783871 + ] + ], + "st_conc_latency_p95_list_list": [ + [ + 0.103295, + 0.115455, + 0.121151 + ], + [ + 0.027967, + 0.044607, + 0.063839 + ], + [ + 0.028175, + 0.045951, + 0.065151 + ] + ], + "st_conc_latency_avg_list_list": [ + [ + 0.04936760449523388, + 0.06972336395202725, + 0.07444538541092975 + ], + [ + 0.02068985204345577, + 0.02825433607535321, + 0.037233642103957326 + ], + [ + 0.020995807770388515, + 0.028403830945016105, + 0.03740811619860986 + ] + ] + }, + "task_config": { + "db": "TurboPuffer", + "db_config": { + "db_label": "2026-03-31", + "version": "", + "note": "", + "api_key": "**********", + "region": "aws-us-west-2", + "api_base_url": null, + "namespace": "vdbbench_test_streaming_500" + }, + "db_case_config": { + "metric_type": "COSINE", + "use_multi_ns_for_filter": false, + "time_wait_warmup": 60 + }, + "case_config": { + "case_id": 200, + "custom_case": { + "dataset_with_size_type": "Large Cohere (768dim, 10M)", + "insert_rate": 500, + "bulk_insert_ratio": 0.8 + }, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "drop_old", + "load", + "search_serial", + "search_concurrent" + ] + }, + "label": ":)" + } + ], + "file_fmt": "result_{}_{}_{}.json", + "timestamp": 1775001600.0 +} \ No newline at end of file diff --git a/vectordb_bench/results/ZillizCloud/result_20260403_standard_zillizcloud.json b/vectordb_bench/results/ZillizCloud/result_20260403_standard_zillizcloud.json new file mode 100644 index 000000000..1f18a729a --- /dev/null +++ b/vectordb_bench/results/ZillizCloud/result_20260403_standard_zillizcloud.json @@ -0,0 +1,2150 @@ +{ + "run_id": "c11e83b51ff14060a08f06d58f801214", + "task_label": "standard_20260403", + "results": [ + { + "metrics": { + "max_load_count": 0, + "insert_duration": 246.6425, + "optimize_duration": 3135.662, + "load_duration": 3382.3046, + "qps": 13316.2336, + "serial_latency_p99": 0.002, + "serial_latency_p95": 0.0019, + "recall": 0.9383, + "ndcg": 0.9484, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 557.5439, + 2665.8166, + 5047.6131, + 8707.1886, + 10381.4448, + 11435.3821, + 12833.6289, + 13316.2336 + ], + "conc_latency_p99_list": [ + 0.001960459982510656, + 0.002204382496420288, + 0.0025926707847975195, + 0.003785052003804594, + 0.005270074445288626, + 0.006814960413612425, + 0.009530203816248103, + 0.01240227443340701 + ], + "conc_latency_p95_list": [ + 0.0018868701998144388, + 0.0020324485929450018, + 0.0021866516792215405, + 0.002869376807939261, + 0.004040546333999372, + 0.0052645970426965505, + 0.007324896720820107, + 0.009507914245477877 + ], + "conc_latency_avg_list": [ + 0.0017909009081054225, + 0.0018719543108110278, + 0.001976279835245286, + 0.002288533358936022, + 0.0028752306497382865, + 0.0034729409658481964, + 0.004612408416183334, + 0.0058844483816759795 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 1, + "num_shards": 1 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 246.6425, + "optimize_duration": 3135.662, + "load_duration": 3382.3046, + "qps": 12837.5287, + "serial_latency_p99": 0.0021, + "serial_latency_p95": 0.002, + "recall": 0.9588, + "ndcg": 0.9657, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 539.7039, + 2582.4122, + 4917.6589, + 8479.8846, + 10116.1707, + 11068.2155, + 12229.2208, + 12837.5287 + ], + "conc_latency_p99_list": [ + 0.0020551326422719287, + 0.002271793531253935, + 0.0026150090881856096, + 0.00380311052547768, + 0.005449252330581657, + 0.0069998929975554295, + 0.010041847208049142, + 0.012852289543952794 + ], + "conc_latency_p95_list": [ + 0.0019641239661723374, + 0.0021013408317230643, + 0.002235009270953014, + 0.002924549448653124, + 0.004163405022700318, + 0.0054554910166189075, + 0.007754770980682224, + 0.009949398809112607 + ], + "conc_latency_avg_list": [ + 0.0018501577211713547, + 0.001932346768512776, + 0.0020285184508130197, + 0.0023497830070767353, + 0.002947098563192882, + 0.003586949439268913, + 0.0048461200425519, + 0.006100907381850077 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 2, + "num_shards": 1 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 246.6425, + "optimize_duration": 3135.662, + "load_duration": 3382.3046, + "qps": 12248.9154, + "serial_latency_p99": 0.0022, + "serial_latency_p95": 0.0021, + "recall": 0.9687, + "ndcg": 0.9742, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 526.2995, + 2512.1902, + 4765.4856, + 8188.3548, + 9485.6147, + 10432.6026, + 11820.0344, + 12248.9154 + ], + "conc_latency_p99_list": [ + 0.0021030055452138188, + 0.0023121346672996877, + 0.0026887326262658466, + 0.003929865991231055, + 0.005846867947839196, + 0.007567329368903298, + 0.01027507190126926, + 0.013403068128973255 + ], + "conc_latency_p95_list": [ + 0.002018616924760863, + 0.00216002133092843, + 0.0023071649571647867, + 0.003043863424682058, + 0.004536616246332414, + 0.005861608259147033, + 0.008004442710080184, + 0.010495775798335672 + ], + "conc_latency_avg_list": [ + 0.0018973754593796914, + 0.001986612150848518, + 0.0020934129605979786, + 0.0024318237152467248, + 0.0031476610878461374, + 0.00380841888066139, + 0.005011055927151683, + 0.006389479412319173 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 3, + "num_shards": 1 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 246.6425, + "optimize_duration": 3135.662, + "load_duration": 3382.3046, + "qps": 11501.6652, + "serial_latency_p99": 0.0022, + "serial_latency_p95": 0.0022, + "recall": 0.9785, + "ndcg": 0.9825, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 498.953, + 2391.8074, + 4531.3051, + 7692.4049, + 8962.935, + 9790.7556, + 10733.1679, + 11501.6652 + ], + "conc_latency_p99_list": [ + 0.002246916518197395, + 0.002433281935518607, + 0.0027782795106759276, + 0.004184300593915395, + 0.006221411311416887, + 0.007918057614006104, + 0.011295880685211153, + 0.013962703556753681 + ], + "conc_latency_p95_list": [ + 0.002148064094944857, + 0.002272934094071388, + 0.00242809351766482, + 0.0032858662889339025, + 0.0048346503434004255, + 0.006268709182040765, + 0.008884398700320158, + 0.011115554475691165 + ], + "conc_latency_avg_list": [ + 0.0020014255442295315, + 0.002086812448327015, + 0.0022017174912128874, + 0.0025910515942768643, + 0.0033292378315385594, + 0.0040559394841899535, + 0.005528263390600377, + 0.006816899394463919 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 4, + "num_shards": 1 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 246.6425, + "optimize_duration": 3135.662, + "load_duration": 3382.3046, + "qps": 10566.6823, + "serial_latency_p99": 0.0024, + "serial_latency_p95": 0.0023, + "recall": 0.9838, + "ndcg": 0.9868, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 473.2503, + 2266.5174, + 4325.3899, + 7263.7246, + 8352.1369, + 9198.3778, + 10210.3429, + 10566.6823 + ], + "conc_latency_p99_list": [ + 0.0023700519639533015, + 0.0025644300674321128, + 0.0029225554369622857, + 0.0044386597932316255, + 0.00657977487426251, + 0.008452395589556558, + 0.011632206621579828, + 0.015279923828202287 + ], + "conc_latency_p95_list": [ + 0.0022803591098636386, + 0.002403592297923751, + 0.002544367348309606, + 0.0034909876121673724, + 0.005244998395210129, + 0.0067171422066167, + 0.009315501491073517, + 0.01212728061946109 + ], + "conc_latency_avg_list": [ + 0.0021102672976180386, + 0.0022023083714165855, + 0.0023067257051930207, + 0.0027442442136128803, + 0.003573842319898506, + 0.004316896363141895, + 0.005797124719790328, + 0.007427947098361652 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 5, + "num_shards": 1 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 246.6425, + "optimize_duration": 3135.662, + "load_duration": 3382.3046, + "qps": 9227.318, + "serial_latency_p99": 0.0027, + "serial_latency_p95": 0.0026, + "recall": 0.9893, + "ndcg": 0.9915, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 427.7206, + 2082.4408, + 3978.3343, + 6561.5406, + 7528.8901, + 8016.929, + 8877.3559, + 9227.318 + ], + "conc_latency_p99_list": [ + 0.002635034592822194, + 0.002782075599534437, + 0.003112639953033066, + 0.0048944263125304125, + 0.00728108051931485, + 0.009557482128147962, + 0.013117193853249772, + 0.017160092100966726 + ], + "conc_latency_p95_list": [ + 0.0025485639926046133, + 0.0026276092685293406, + 0.002766418919782154, + 0.003919153648894278, + 0.005883905396331093, + 0.007771545994910409, + 0.010690590104786672, + 0.013842354586813595 + ], + "conc_latency_avg_list": [ + 0.002334789800170702, + 0.0023967380922842478, + 0.002508067757169752, + 0.0030384944076291527, + 0.003962180586705604, + 0.004954530890871444, + 0.006679939886193658, + 0.00850275890671233 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 6, + "num_shards": 1 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 246.6425, + "optimize_duration": 3135.662, + "load_duration": 3382.3046, + "qps": 8320.4606, + "serial_latency_p99": 0.0029, + "serial_latency_p95": 0.0028, + "recall": 0.9919, + "ndcg": 0.9936, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 396.8576, + 1911.4443, + 3643.8766, + 5864.3359, + 6725.025, + 7313.0633, + 7887.1755, + 8320.4606 + ], + "conc_latency_p99_list": [ + 0.002857399091590195, + 0.0030318665952654557, + 0.003405487750424072, + 0.00554735084413551, + 0.00816060005221516, + 0.010382328977575536, + 0.014569980294909328, + 0.019027257570996884 + ], + "conc_latency_p95_list": [ + 0.002767942799255252, + 0.0028832201816840096, + 0.0030418253620155154, + 0.004497910931240768, + 0.006637068011332303, + 0.008489592577097936, + 0.011977570212911815, + 0.015408052015118301 + ], + "conc_latency_avg_list": [ + 0.0025167798504936094, + 0.0026116185368739234, + 0.0027388090118584786, + 0.003400314036561817, + 0.0044390573829028, + 0.005437121205254614, + 0.0075142181236358555, + 0.009432022524963333 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 7, + "num_shards": 1 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 246.6425, + "optimize_duration": 3135.662, + "load_duration": 3382.3046, + "qps": 7524.9879, + "serial_latency_p99": 0.0032, + "serial_latency_p95": 0.0031, + "recall": 0.9931, + "ndcg": 0.9946, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 363.7387, + 1761.1002, + 3372.7075, + 5421.2445, + 6091.3075, + 6579.0629, + 7061.7035, + 7524.9879 + ], + "conc_latency_p99_list": [ + 0.0031524766457732764, + 0.0032982631359482185, + 0.0036804132821271207, + 0.006010826830170116, + 0.008973520016297695, + 0.011436859995592378, + 0.016333586145192387, + 0.020569344094838005 + ], + "conc_latency_p95_list": [ + 0.0030544937413651495, + 0.0031513541325693946, + 0.0033083077374612907, + 0.004909411718836053, + 0.007369707978796214, + 0.009455612045712769, + 0.013346620369702576, + 0.01678576849226374 + ], + "conc_latency_avg_list": [ + 0.0027460860977463145, + 0.0028345832649254296, + 0.0029595516874138823, + 0.003678043908615719, + 0.004902225199747265, + 0.006042755015168581, + 0.008396028243715591, + 0.010450248464720832 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 8, + "num_shards": 1 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 246.6425, + "optimize_duration": 3135.662, + "load_duration": 3382.3046, + "qps": 6813.2439, + "serial_latency_p99": 0.0035, + "serial_latency_p95": 0.0034, + "recall": 0.9939, + "ndcg": 0.9953, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 336.5978, + 1642.6462, + 3138.7921, + 4969.8163, + 5673.7771, + 6090.0396, + 6493.0362, + 6813.2439 + ], + "conc_latency_p99_list": [ + 0.003431524743209593, + 0.003548497949959711, + 0.0039453561976552, + 0.006659876625053583, + 0.009535451157134956, + 0.012163680926314556, + 0.017726751818554484, + 0.022734694816172126 + ], + "conc_latency_p95_list": [ + 0.0033281981566688048, + 0.0034026612091111017, + 0.0035765988053753973, + 0.005437539250124246, + 0.007899306231411173, + 0.0101619676919654, + 0.014541133219609037, + 0.01846120802219957 + ], + "conc_latency_avg_list": [ + 0.0029676403704890024, + 0.0030391423281907995, + 0.003179950204961118, + 0.0040130850174019085, + 0.005263782041679775, + 0.006520409861910488, + 0.009132446429719516, + 0.011525998827399965 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 9, + "num_shards": 1 + }, + "case_config": { + "case_id": 5, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 2451.8899, + "optimize_duration": 4862.256, + "load_duration": 7314.1459, + "qps": 7385.2066, + "serial_latency_p99": 0.0021, + "serial_latency_p95": 0.002, + "recall": 0.9384, + "ndcg": 0.9441, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 505.8686, + 2189.7181, + 3241.0071, + 4661.5936, + 5380.3716, + 5932.9765, + 6767.3029, + 7385.2066 + ], + "conc_latency_p99_list": [ + 0.0022006261744536458, + 0.002760201054625213, + 0.0037472858920227733, + 0.0067906589363701635, + 0.010014167174231262, + 0.012363597416551783, + 0.016403084830380974, + 0.01993567283730954 + ], + "conc_latency_p95_list": [ + 0.002098581724567339, + 0.002593909669667482, + 0.0035126500617479904, + 0.005520994003745727, + 0.008371637808158994, + 0.010467902664095164, + 0.01375423202989623, + 0.016782393981702625 + ], + "conc_latency_avg_list": [ + 0.001973905573358728, + 0.0022795015833306665, + 0.0030794282993742857, + 0.004278176924915009, + 0.005550848216261312, + 0.0066920424895898075, + 0.008768764746552707, + 0.010642102641863554 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 1, + "num_shards": 1 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 2451.8899, + "optimize_duration": 4862.256, + "load_duration": 7314.1459, + "qps": 6793.8443, + "serial_latency_p99": 0.0022, + "serial_latency_p95": 0.0021, + "recall": 0.9522, + "ndcg": 0.9568, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 503.9478, + 2114.1911, + 3099.2869, + 4416.7094, + 5150.037, + 5691.2226, + 6289.7121, + 6793.8443 + ], + "conc_latency_p99_list": [ + 0.00215859985910356, + 0.002847761964658275, + 0.003923368623363787, + 0.007356263077235778, + 0.010528954989276825, + 0.01292447085143066, + 0.01769668879453093, + 0.021927826073952026 + ], + "conc_latency_p95_list": [ + 0.002082229100051336, + 0.0026818424608791246, + 0.0036669230728875847, + 0.005921932504861616, + 0.00885509350337088, + 0.010938107722904525, + 0.014863643678836526, + 0.018132617010269306 + ], + "conc_latency_avg_list": [ + 0.001981333490019148, + 0.002360769086835183, + 0.003220095266754697, + 0.004516460962995695, + 0.005803644141310085, + 0.006980248662568344, + 0.009430996778203294, + 0.011560318047614999 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 2, + "num_shards": 1 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 2451.8899, + "optimize_duration": 4862.256, + "load_duration": 7314.1459, + "qps": 6242.5346, + "serial_latency_p99": 0.0023, + "serial_latency_p95": 0.0022, + "recall": 0.961, + "ndcg": 0.965, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 446.4866, + 2002.9326, + 2953.3925, + 4164.4285, + 4805.5397, + 5240.789, + 5870.8363, + 6242.5346 + ], + "conc_latency_p99_list": [ + 0.002856350458459926, + 0.0029789366439217715, + 0.004114359063096344, + 0.007887981488602236, + 0.011494717993773522, + 0.014308837521821265, + 0.019093695696210486, + 0.023705490870634095 + ], + "conc_latency_p95_list": [ + 0.0025365250825416293, + 0.0028055029979441315, + 0.0038476928253658115, + 0.0064046091894852, + 0.009612656582612544, + 0.012057665473548695, + 0.01607741924817674, + 0.019808521203231066 + ], + "conc_latency_avg_list": [ + 0.0022367817378751067, + 0.0024923140599496687, + 0.003379658321121232, + 0.0047897960603506306, + 0.0062167658101995584, + 0.007583969576561268, + 0.010102243992110408, + 0.012617357519441232 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 3, + "num_shards": 1 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 2451.8899, + "optimize_duration": 4862.256, + "load_duration": 7314.1459, + "qps": 5779.119, + "serial_latency_p99": 0.0023, + "serial_latency_p95": 0.0023, + "recall": 0.971, + "ndcg": 0.9742, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 476.6126, + 1920.6379, + 2828.6202, + 3905.7911, + 4495.2986, + 4903.9634, + 5352.3634, + 5779.119 + ], + "conc_latency_p99_list": [ + 0.002284108918393031, + 0.003114189200568944, + 0.004329544047359377, + 0.008613102727103977, + 0.012333529423922296, + 0.015281949284835726, + 0.020651346329832433, + 0.025012934936676175 + ], + "conc_latency_p95_list": [ + 0.0022121331770904363, + 0.0029337720014154913, + 0.004045496415346861, + 0.007010379375424236, + 0.010350381198804826, + 0.012867126709898001, + 0.01739263068011496, + 0.021097359794657676 + ], + "conc_latency_avg_list": [ + 0.002095217670483641, + 0.0025990775656036295, + 0.003529053754031682, + 0.005105319901460065, + 0.006648317818211828, + 0.008106390568584667, + 0.011082872888195554, + 0.01359851345251022 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 4, + "num_shards": 1 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 2451.8899, + "optimize_duration": 4862.256, + "load_duration": 7314.1459, + "qps": 5183.5843, + "serial_latency_p99": 0.0024, + "serial_latency_p95": 0.0023, + "recall": 0.9784, + "ndcg": 0.981, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 456.5329, + 1821.263, + 2677.2938, + 3617.961, + 4097.4679, + 4447.7515, + 4863.2316, + 5183.5843 + ], + "conc_latency_p99_list": [ + 0.002395828692242503, + 0.0033142844232497736, + 0.004636971018044278, + 0.009431733234669086, + 0.013644934630719942, + 0.01654091664124283, + 0.022408935175044466, + 0.027210572804324325 + ], + "conc_latency_p95_list": [ + 0.00232212619157508, + 0.0031105756846955047, + 0.0043217132915742695, + 0.007749295464600434, + 0.011374025803525001, + 0.01404695180244743, + 0.01901522108237258, + 0.022979849949479103 + ], + "conc_latency_avg_list": [ + 0.0021874608272499383, + 0.0027410038616561545, + 0.003728350102575746, + 0.005515110815448404, + 0.0072920914670729624, + 0.008931282929280606, + 0.012187039287903855, + 0.015173242426486785 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 5, + "num_shards": 1 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 2451.8899, + "optimize_duration": 4862.256, + "load_duration": 7314.1459, + "qps": 4259.5572, + "serial_latency_p99": 0.0026, + "serial_latency_p95": 0.0026, + "recall": 0.9845, + "ndcg": 0.9866, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 424.077, + 1676.9478, + 2434.6516, + 3184.2011, + 3571.9199, + 3767.5544, + 4061.1698, + 4259.5572 + ], + "conc_latency_p99_list": [ + 0.002595295326318592, + 0.003621646617539228, + 0.005367588647641237, + 0.011171487247338519, + 0.0155316284415312, + 0.019644083841703817, + 0.02703630503267048, + 0.03310978163208347 + ], + "conc_latency_p95_list": [ + 0.002518506458727643, + 0.0034135113819502294, + 0.004873151995707303, + 0.009142277223872952, + 0.013012332818470895, + 0.016382047021761527, + 0.022542749025160444, + 0.027544378107995725 + ], + "conc_latency_avg_list": [ + 0.00235500177392571, + 0.0029770619258879757, + 0.00410082317131261, + 0.006266042699984648, + 0.008367804647869575, + 0.010561791085210772, + 0.01460435324245386, + 0.018471206884305532 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 6, + "num_shards": 1 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 2451.8899, + "optimize_duration": 4862.256, + "load_duration": 7314.1459, + "qps": 3614.3118, + "serial_latency_p99": 0.0029, + "serial_latency_p95": 0.0028, + "recall": 0.9877, + "ndcg": 0.9894, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 397.2298, + 1557.1023, + 2236.3239, + 2845.1058, + 3146.0016, + 3289.0762, + 3520.8116, + 3614.3118 + ], + "conc_latency_p99_list": [ + 0.0027946092444472016, + 0.003967489110655152, + 0.00615017178060953, + 0.0128463427053066, + 0.017453623053152116, + 0.022870610732934445, + 0.029518100013956432, + 0.03869990034552756 + ], + "conc_latency_p95_list": [ + 0.0027128490241011606, + 0.003724151346250437, + 0.0054281810007523745, + 0.01044328572170343, + 0.01436160156154074, + 0.01821845376980491, + 0.024413908016867936, + 0.031903167749987915 + ], + "conc_latency_avg_list": [ + 0.0025143936207342324, + 0.003206745084255346, + 0.004464954355791834, + 0.007013879446545796, + 0.009503234791544602, + 0.012096279585844553, + 0.01685092675726034, + 0.021781507836163158 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 7, + "num_shards": 1 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 2451.8899, + "optimize_duration": 4862.256, + "load_duration": 7314.1459, + "qps": 3181.0537, + "serial_latency_p99": 0.003, + "serial_latency_p95": 0.0029, + "recall": 0.9892, + "ndcg": 0.9908, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 373.2168, + 1461.7612, + 2063.4765, + 2583.7979, + 2781.1167, + 2908.4205, + 3073.9858, + 3181.0537 + ], + "conc_latency_p99_list": [ + 0.003002641891944222, + 0.004314229479641651, + 0.007060362810152582, + 0.01453221907839178, + 0.01971446685201954, + 0.024677592392545203, + 0.032532848112750784, + 0.04069755139702464 + ], + "conc_latency_p95_list": [ + 0.0029138700163457544, + 0.00403262600011658, + 0.0060169674427015705, + 0.011576333804987366, + 0.01587381720310077, + 0.019931912398897106, + 0.027404746599495412, + 0.03406435703218449 + ], + "conc_latency_avg_list": [ + 0.002676176823972926, + 0.0034160777606854604, + 0.004838368703871575, + 0.0077237947842559935, + 0.010749740541723023, + 0.013688260722494459, + 0.01930447747175536, + 0.024747977200758154 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 8, + "num_shards": 1 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + }, + { + "metrics": { + "max_load_count": 0, + "insert_duration": 2451.8899, + "optimize_duration": 4862.256, + "load_duration": 7314.1459, + "qps": 2804.8357, + "serial_latency_p99": 0.0033, + "serial_latency_p95": 0.0032, + "recall": 0.9903, + "ndcg": 0.9918, + "conc_num_list": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "conc_qps_list": [ + 352.7729, + 1367.945, + 1917.0391, + 2338.0313, + 2510.7421, + 2617.9646, + 2727.8716, + 2804.8357 + ], + "conc_latency_p99_list": [ + 0.00323100520123262, + 0.004680905669229106, + 0.00799012375646271, + 0.01590309410821647, + 0.021047461275593433, + 0.025625948917586353, + 0.036803203278686886, + 0.0467973963287659 + ], + "conc_latency_p95_list": [ + 0.0031083543435670435, + 0.0043611170200165365, + 0.0067188970482675355, + 0.0125031745119486, + 0.017026124306721607, + 0.021185590000823137, + 0.03006786260521039, + 0.03819325279910117 + ], + "conc_latency_avg_list": [ + 0.002831514758067566, + 0.003649909442997153, + 0.00520858526610239, + 0.00853725560951756, + 0.011906267087115343, + 0.015190065559178532, + 0.021789805178996344, + 0.0280490530042168 + ], + "st_ideal_insert_duration": 0, + "st_search_stage_list": [], + "st_search_time_list": [], + "st_max_qps_list_list": [], + "st_recall_list": [], + "st_ndcg_list": [], + "st_serial_latency_p99_list": [], + "st_serial_latency_p95_list": [], + "st_conc_failed_rate_list": [], + "st_conc_num_list_list": [], + "st_conc_qps_list_list": [], + "st_conc_latency_p99_list_list": [], + "st_conc_latency_p95_list_list": [], + "st_conc_latency_avg_list_list": [] + }, + "task_config": { + "db": "ZillizCloud", + "db_config": { + "db_label": "8cu-perf", + "version": "v2026.4", + "note": "", + "uri": "**********", + "user": "root", + "password": "**********", + "collection_name": "ZillizCloudVDBBench" + }, + "db_case_config": { + "index": "AUTOINDEX", + "metric_type": "COSINE", + "use_partition_key": false, + "level": 9, + "num_shards": 1 + }, + "case_config": { + "case_id": 4, + "custom_case": {}, + "k": 100, + "concurrency_search_config": { + "num_concurrency": [ + 1, + 5, + 10, + 20, + 30, + 40, + 60, + 80 + ], + "concurrency_duration": 30, + "concurrency_timeout": 3600 + } + }, + "stages": [ + "search_serial", + "search_concurrent" + ] + } + } + ], + "file_fmt": "result_{}_{}_{}.json", + "timestamp": 1770595200.0 +} \ No newline at end of file diff --git a/vectordb_bench/results/getLeaderboardData.py b/vectordb_bench/results/getLeaderboardData.py index aef024bdc..1650a6f87 100644 --- a/vectordb_bench/results/getLeaderboardData.py +++ b/vectordb_bench/results/getLeaderboardData.py @@ -1,14 +1,16 @@ -from vectordb_bench import config -import ujson import pathlib +from datetime import datetime + +import ujson + +from vectordb_bench import config from vectordb_bench.backend.cases import CaseType from vectordb_bench.backend.clients import DB from vectordb_bench.frontend.config.dbPrices import DB_DBLABEL_TO_PRICE from vectordb_bench.interface import benchMarkRunner from vectordb_bench.models import ResultLabel, TestResult -from datetime import datetime -taskLabelToCode = { +task_label_to_code = { ResultLabel.FAILED: -1, ResultLabel.OUTOFRANGE: -2, ResultLabel.NORMAL: 1, @@ -18,15 +20,14 @@ def format_time(ts: float) -> str: default_standard_test_time = datetime(2023, 8, 1) t = datetime.fromtimestamp(ts) - if t < default_standard_test_time: - t = default_standard_test_time + t = max(t, default_standard_test_time) return t.strftime("%Y-%m") def main(): - allResults: list[TestResult] = benchMarkRunner.get_results() + all_results: list[TestResult] = benchMarkRunner.get_results() - if allResults is not None: + if all_results is not None: data = [ { "db": d.task_config.db.value, @@ -36,18 +37,16 @@ def main(): "qps": d.metrics.qps, "latency": d.metrics.serial_latency_p99, "recall": d.metrics.recall, - "label": taskLabelToCode[d.label], + "label": task_label_to_code[d.label], "note": d.task_config.db_config.note, "version": d.task_config.db_config.version, "test_time": format_time(test_result.timestamp), } - for test_result in allResults + for test_result in all_results if "standard" in test_result.task_label for d in test_result.results - if d.task_config.case_config.case_id != CaseType.CapacityDim128 - and d.task_config.case_config.case_id != CaseType.CapacityDim960 - if d.task_config.db != DB.ZillizCloud - or test_result.timestamp >= datetime(2024, 1, 1).timestamp() + if d.task_config.case_config.case_id not in {CaseType.CapacityDim128, CaseType.CapacityDim960} + if d.task_config.db != DB.ZillizCloud or test_result.timestamp >= datetime(2024, 1, 1).timestamp() ] # compute qp$ @@ -58,7 +57,7 @@ def main(): price = DB_DBLABEL_TO_PRICE.get(db, {}).get(db_label, 0) d["qp$"] = (qps / price * 3600) if price > 0 else 0.0 - with open(pathlib.Path(config.RESULTS_LOCAL_DIR, "leaderboard.json"), "w") as f: + with pathlib.Path(config.RESULTS_LOCAL_DIR, "leaderboard.json").open("w") as f: ujson.dump(data, f) diff --git a/vectordb_bench/results/getLeaderboardDataV2.py b/vectordb_bench/results/getLeaderboardDataV2.py index 62440886f..188d9876f 100644 --- a/vectordb_bench/results/getLeaderboardDataV2.py +++ b/vectordb_bench/results/getLeaderboardDataV2.py @@ -1,17 +1,14 @@ import json import logging +import pathlib - +from vectordb_bench import config from vectordb_bench.backend.cases import CaseType, StreamingPerformanceCase -from vectordb_bench.backend.clients import DB +from vectordb_bench.interface import BenchMarkRunner from vectordb_bench.models import CaseResult -from vectordb_bench import config -import numpy as np logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s") -from vectordb_bench.interface import BenchMarkRunner - def get_standard_2025_results() -> list[CaseResult]: all_results = BenchMarkRunner.get_results() @@ -23,7 +20,7 @@ def get_standard_2025_results() -> list[CaseResult]: def save_to_json(data: list[dict], file_name: str): - with open(file_name, "w") as f: + with pathlib.Path(file_name).open("w") as f: json.dump(data, f, indent=4) @@ -56,11 +53,11 @@ def main(): } ) else: - case: StreamingPerformanceCase = case + streaming_case: StreamingPerformanceCase = case # use 90p search stage results to represent streaming performance qps_90p = metrics.st_max_qps_list_list[metrics.st_search_stage_list.index(90)] latency_90p = metrics.st_serial_latency_p99_list[metrics.st_search_stage_list.index(90)] - insert_rate = case.insert_rate + insert_rate = streaming_case.insert_rate streaming_data.append( { "dataset": dataset, diff --git a/vectordb_bench/results/leaderboard_v2.json b/vectordb_bench/results/leaderboard_v2.json index e4f8dece5..c987ac9e7 100644 --- a/vectordb_bench/results/leaderboard_v2.json +++ b/vectordb_bench/results/leaderboard_v2.json @@ -1,172 +1,12 @@ [ - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 597.3641, - "latency": 12.1, - "recall": 0.9221, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 551.1757, - "latency": 10.7, - "recall": 0.9327, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 492.9765, - "latency": 13.4, - "recall": 0.9443, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 431.0155, - "latency": 15.2, - "recall": 0.954, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 377.3634, - "latency": 16.2, - "recall": 0.9615, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 337.5819, - "latency": 15.0, - "recall": 0.9666, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 279.7257, - "latency": 18.1, - "recall": 0.9729, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g-routing-64shard", - "db_name": "ElasticCloud-8c60g-routing-64shard", - "qps": 3033.786, - "latency": 8.7, - "recall": 0.9934, - "filter_ratio": 0.999 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g-routing-64shard", - "db_name": "ElasticCloud-8c60g-routing-64shard", - "qps": 3019.2416, - "latency": 9.5, - "recall": 0.9765, - "filter_ratio": 0.998 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g-routing-64shard", - "db_name": "ElasticCloud-8c60g-routing-64shard", - "qps": 2890.9523, - "latency": 9.4, - "recall": 0.9625, - "filter_ratio": 0.995 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g-routing-64shard", - "db_name": "ElasticCloud-8c60g-routing-64shard", - "qps": 2789.7212, - "latency": 8.2, - "recall": 0.9538, - "filter_ratio": 0.99 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g-routing-64shard", - "db_name": "ElasticCloud-8c60g-routing-64shard", - "qps": 2457.2628, - "latency": 9.0, - "recall": 0.9378, - "filter_ratio": 0.98 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g-routing-64shard", - "db_name": "ElasticCloud-8c60g-routing-64shard", - "qps": 2209.4973, - "latency": 13.7, - "recall": 0.9228, - "filter_ratio": 0.95 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g-routing-64shard", - "db_name": "ElasticCloud-8c60g-routing-64shard", - "qps": 1960.388, - "latency": 11.0, - "recall": 0.9076, - "filter_ratio": 0.9 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g-routing-64shard", - "db_name": "ElasticCloud-8c60g-routing-64shard", - "qps": 1725.092, - "latency": 11.7, - "recall": 0.8969, - "filter_ratio": 0.8 - }, - { - "dataset": "Cohere (Medium)", - "db": "ElasticCloud", - "label": "8c60g-routing-64shard", - "db_name": "ElasticCloud-8c60g-routing-64shard", - "qps": 1307.419, - "latency": 12.3, - "recall": 0.8925, - "filter_ratio": 0.5 - }, { "dataset": "Cohere (Large)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 1520.4145, - "latency": 12.5, - "recall": 0.9028, + "qps": 2181.3939, + "latency": 9.4, + "recall": 0.8501, "filter_ratio": 0.0 }, { @@ -174,9 +14,9 @@ "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 1273.3452, - "latency": 12.3, - "recall": 0.9242, + "qps": 1721.5416, + "latency": 9.6, + "recall": 0.8855, "filter_ratio": 0.0 }, { @@ -184,9 +24,9 @@ "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 1011.7943, - "latency": 15.2, - "recall": 0.945, + "qps": 1452.1536, + "latency": 10.8, + "recall": 0.9042, "filter_ratio": 0.0 }, { @@ -194,9 +34,9 @@ "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 824.5097, - "latency": 15.5, - "recall": 0.9558, + "qps": 1295.5543, + "latency": 11.2, + "recall": 0.9176, "filter_ratio": 0.0 }, { @@ -204,50 +44,50 @@ "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 350.0132, - "latency": 29.7, - "recall": 1.0, - "filter_ratio": 0.999 + "qps": 1150.4393, + "latency": 13.4, + "recall": 0.9265, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 179.5204, - "latency": 51.4, - "recall": 1.0, - "filter_ratio": 0.998 + "qps": 1032.9696, + "latency": 14.8, + "recall": 0.933, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 72.99, - "latency": 111.4, - "recall": 1.0, - "filter_ratio": 0.995 + "qps": 125.6164, + "latency": 69.8, + "recall": 0.8746, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Large)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 42.9877, - "latency": 201.9, - "recall": 0.9912, - "filter_ratio": 0.99 + "qps": 229.0379, + "latency": 43.0, + "recall": 0.8908, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Large)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 96.4987, - "latency": 113.1, - "recall": 0.9296, - "filter_ratio": 0.98 + "qps": 246.7071, + "latency": 45.1, + "recall": 0.9018, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Large)", @@ -264,129 +104,59 @@ "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 246.7071, - "latency": 45.1, - "recall": 0.9018, - "filter_ratio": 0.9 + "qps": 96.4987, + "latency": 113.1, + "recall": 0.9296, + "filter_ratio": 0.98 }, { "dataset": "Cohere (Large)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 229.0379, - "latency": 43.0, - "recall": 0.8908, - "filter_ratio": 0.8 + "qps": 42.9877, + "latency": 201.9, + "recall": 0.9912, + "filter_ratio": 0.99 }, { "dataset": "Cohere (Large)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 125.6164, - "latency": 69.8, - "recall": 0.8746, - "filter_ratio": 0.5 - }, - { - "dataset": "Cohere (Large)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 376.3752, - "latency": 14.5, - "recall": 0.9039, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Large)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 341.2325, - "latency": 13.4, - "recall": 0.9136, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Large)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 300.7678, - "latency": 12.8, - "recall": 0.922, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Large)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 257.0398, - "latency": 15.4, - "recall": 0.9303, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Large)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 228.7734, - "latency": 16.2, - "recall": 0.9374, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Large)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 204.3654, - "latency": 18.2, - "recall": 0.9424, - "filter_ratio": 0.0 - }, - { - "dataset": "Cohere (Large)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 167.5075, - "latency": 18.0, - "recall": 0.9501, - "filter_ratio": 0.0 + "qps": 72.99, + "latency": 111.4, + "recall": 1.0, + "filter_ratio": 0.995 }, { "dataset": "Cohere (Large)", "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 146.339, - "latency": 20.9, - "recall": 0.9557, - "filter_ratio": 0.0 + "label": "8c60g-force_merge", + "db_name": "ElasticCloud-8c60g-force_merge", + "qps": 179.5204, + "latency": 51.4, + "recall": 1.0, + "filter_ratio": 0.998 }, { "dataset": "Cohere (Large)", "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "qps": 129.0705, - "latency": 24.3, - "recall": 0.96, - "filter_ratio": 0.0 + "label": "8c60g-force_merge", + "db_name": "ElasticCloud-8c60g-force_merge", + "qps": 350.0132, + "latency": 29.7, + "recall": 1.0, + "filter_ratio": 0.999 }, { "dataset": "Cohere (Medium)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 2095.7067, - "latency": 12.4, - "recall": 0.8961, + "qps": 2808.2421, + "latency": 9.5, + "recall": 0.8815, "filter_ratio": 0.0 }, { @@ -394,9 +164,9 @@ "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 1925.3019, - "latency": 11.3, - "recall": 0.9141, + "qps": 2353.8935, + "latency": 17.1, + "recall": 0.9143, "filter_ratio": 0.0 }, { @@ -404,9 +174,9 @@ "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 1707.8841, - "latency": 10.0, - "recall": 0.9314, + "qps": 2030.4249, + "latency": 10.6, + "recall": 0.9306, "filter_ratio": 0.0 }, { @@ -414,9 +184,9 @@ "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 1442.0638, - "latency": 10.1, - "recall": 0.9482, + "qps": 1804.8996, + "latency": 12.3, + "recall": 0.9405, "filter_ratio": 0.0 }, { @@ -424,9 +194,9 @@ "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 1115.106, - "latency": 13.1, - "recall": 0.9662, + "qps": 1623.8421, + "latency": 11.8, + "recall": 0.9479, "filter_ratio": 0.0 }, { @@ -434,9 +204,9 @@ "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 910.4322, - "latency": 14.2, - "recall": 0.9748, + "qps": 1482.3772, + "latency": 12.1, + "recall": 0.9546, "filter_ratio": 0.0 }, { @@ -444,40 +214,40 @@ "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 2175.2694, - "latency": 9.8, - "recall": 1.0, - "filter_ratio": 0.999 + "qps": 899.3114, + "latency": 14.9, + "recall": 0.9072, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Medium)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 1430.0244, - "latency": 12.6, - "recall": 1.0, - "filter_ratio": 0.998 + "qps": 669.9441, + "latency": 19.1, + "recall": 0.9227, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Medium)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 692.5751, - "latency": 18.7, - "recall": 1.0, - "filter_ratio": 0.995 + "qps": 437.8735, + "latency": 27.1, + "recall": 0.9364, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Medium)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 364.3516, - "latency": 26.4, - "recall": 1.0, - "filter_ratio": 0.99 + "qps": 249.3519, + "latency": 44.7, + "recall": 0.9446, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Medium)", @@ -494,470 +264,520 @@ "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 249.3519, - "latency": 44.7, - "recall": 0.9446, - "filter_ratio": 0.95 + "qps": 364.3516, + "latency": 26.4, + "recall": 1.0, + "filter_ratio": 0.99 }, { "dataset": "Cohere (Medium)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 437.8735, - "latency": 27.1, - "recall": 0.9364, - "filter_ratio": 0.9 + "qps": 692.5751, + "latency": 18.7, + "recall": 1.0, + "filter_ratio": 0.995 }, { "dataset": "Cohere (Medium)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 669.9441, - "latency": 19.1, - "recall": 0.9227, - "filter_ratio": 0.8 + "qps": 1430.0244, + "latency": 12.6, + "recall": 1.0, + "filter_ratio": 0.998 }, { "dataset": "Cohere (Medium)", "db": "ElasticCloud", "label": "8c60g-force_merge", "db_name": "ElasticCloud-8c60g-force_merge", - "qps": 899.3114, - "latency": 14.9, - "recall": 0.9072, + "qps": 2175.2694, + "latency": 9.8, + "recall": 1.0, + "filter_ratio": 0.999 + }, + { + "dataset": "Cohere (Medium)", + "db": "ElasticCloud", + "label": "8c60g-routing-64shard", + "db_name": "ElasticCloud-8c60g-routing-64shard", + "qps": 1307.419, + "latency": 12.3, + "recall": 0.8925, "filter_ratio": 0.5 }, { "dataset": "Cohere (Medium)", + "db": "ElasticCloud", + "label": "8c60g-routing-64shard", + "db_name": "ElasticCloud-8c60g-routing-64shard", + "qps": 1725.092, + "latency": 11.7, + "recall": 0.8969, + "filter_ratio": 0.8 + }, + { + "dataset": "Cohere (Medium)", + "db": "ElasticCloud", + "label": "8c60g-routing-64shard", + "db_name": "ElasticCloud-8c60g-routing-64shard", + "qps": 1960.388, + "latency": 11.0, + "recall": 0.9076, + "filter_ratio": 0.9 + }, + { + "dataset": "Cohere (Medium)", + "db": "ElasticCloud", + "label": "8c60g-routing-64shard", + "db_name": "ElasticCloud-8c60g-routing-64shard", + "qps": 2209.4973, + "latency": 13.7, + "recall": 0.9228, + "filter_ratio": 0.95 + }, + { + "dataset": "Cohere (Medium)", + "db": "ElasticCloud", + "label": "8c60g-routing-64shard", + "db_name": "ElasticCloud-8c60g-routing-64shard", + "qps": 2457.2628, + "latency": 9.0, + "recall": 0.9378, + "filter_ratio": 0.98 + }, + { + "dataset": "Cohere (Medium)", + "db": "ElasticCloud", + "label": "8c60g-routing-64shard", + "db_name": "ElasticCloud-8c60g-routing-64shard", + "qps": 2789.7212, + "latency": 8.2, + "recall": 0.9538, + "filter_ratio": 0.99 + }, + { + "dataset": "Cohere (Medium)", + "db": "ElasticCloud", + "label": "8c60g-routing-64shard", + "db_name": "ElasticCloud-8c60g-routing-64shard", + "qps": 2890.9523, + "latency": 9.4, + "recall": 0.9625, + "filter_ratio": 0.995 + }, + { + "dataset": "Cohere (Medium)", + "db": "ElasticCloud", + "label": "8c60g-routing-64shard", + "db_name": "ElasticCloud-8c60g-routing-64shard", + "qps": 3019.2416, + "latency": 9.5, + "recall": 0.9765, + "filter_ratio": 0.998 + }, + { + "dataset": "Cohere (Medium)", + "db": "ElasticCloud", + "label": "8c60g-routing-64shard", + "db_name": "ElasticCloud-8c60g-routing-64shard", + "qps": 3033.786, + "latency": 8.7, + "recall": 0.9934, + "filter_ratio": 0.999 + }, + { + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 3465.1696, - "latency": 2.2, - "recall": 0.9528, + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 3917.2035, + "latency": 2.4, + "recall": 0.9203, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 3102.1518, - "latency": 2.3, - "recall": 0.9608, + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 3628.8527, + "latency": 2.6, + "recall": 0.9318, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 2681.2848, - "latency": 2.5, - "recall": 0.9681, + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 3250.1112, + "latency": 2.7, + "recall": 0.9443, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 2232.9105, - "latency": 2.9, - "recall": 0.9757, + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 2762.4144, + "latency": 3.1, + "recall": 0.9556, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 1913.17, + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 2384.6245, "latency": 3.2, - "recall": 0.9797, + "recall": 0.9627, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 1575.8137, - "latency": 3.5, - "recall": 0.9822, + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 2134.1717, + "latency": 3.8, + "recall": 0.9671, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 1344.5652, - "latency": 4.2, - "recall": 0.9849, + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 1641.3478, + "latency": 4.1, + "recall": 0.9729, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 1148.7813, - "latency": 4.8, - "recall": 0.9861, + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 1488.5841, + "latency": 4.7, + "recall": 0.9764, "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 3680.6045, - "latency": 2.2, - "recall": 0.9954, - "filter_ratio": 0.999 + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 10333.9072, + "latency": 2.0, + "recall": 0.889, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 3407.9972, - "latency": 2.2, - "recall": 0.994, - "filter_ratio": 0.998 + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 9575.6863, + "latency": 2.3, + "recall": 0.9189, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 3062.6755, + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 8596.7694, "latency": 2.4, - "recall": 0.9932, - "filter_ratio": 0.995 + "recall": 0.9416, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 2568.3371, + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 7704.3625, "latency": 2.7, - "recall": 0.9927, - "filter_ratio": 0.99 + "recall": 0.9541, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 1886.2891, - "latency": 3.3, - "recall": 0.992, - "filter_ratio": 0.98 + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 7023.6735, + "latency": 3.0, + "recall": 0.962, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 923.8685, - "latency": 6.7, - "recall": 0.9919, - "filter_ratio": 0.95 + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 6031.3725, + "latency": 3.3, + "recall": 0.971, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 1442.1076, - "latency": 4.1, - "recall": 0.9517, - "filter_ratio": 0.9 + "label": "16c64g-sq4u-fp16-force_merge", + "db_name": "Milvus-16c64g-sq4u-fp16-force_merge", + "qps": 5258.1868, + "latency": 3.6, + "recall": 0.9768, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 2002.9865, - "latency": 3.2, - "recall": 0.9467, - "filter_ratio": 0.8 + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 2747.3167, + "latency": 3.3, + "recall": 0.9204, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 3201.9438, - "latency": 2.3, - "recall": 0.922, - "filter_ratio": 0.5 + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 2514.4481, + "latency": 3.2, + "recall": 0.9303, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8-partition_key", - "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 11763.5538, - "latency": 1.5, - "recall": 1.0, - "filter_ratio": 0.999 + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 2177.2345, + "latency": 3.4, + "recall": 0.9408, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8-partition_key", - "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 11803.1944, - "latency": 1.5, - "recall": 0.9778, - "filter_ratio": 0.998 + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 1833.2575, + "latency": 3.9, + "recall": 0.951, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8-partition_key", - "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 11520.9234, - "latency": 1.5, - "recall": 0.9634, - "filter_ratio": 0.995 + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 1552.4803, + "latency": 4.0, + "recall": 0.9565, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8-partition_key", - "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 11280.0849, - "latency": 1.6, - "recall": 0.9507, - "filter_ratio": 0.99 - }, - { - "dataset": "Cohere (Medium)", - "db": "Milvus", - "label": "16c64g-sq8-partition_key", - "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 10671.8925, - "latency": 1.7, - "recall": 0.9339, - "filter_ratio": 0.98 - }, - { - "dataset": "Cohere (Medium)", - "db": "Milvus", - "label": "16c64g-sq8-partition_key", - "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 10258.2661, - "latency": 1.7, - "recall": 0.9139, - "filter_ratio": 0.95 - }, - { - "dataset": "Cohere (Medium)", - "db": "Milvus", - "label": "16c64g-sq8-partition_key", - "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 9681.5656, - "latency": 1.9, - "recall": 0.9008, - "filter_ratio": 0.9 - }, - { - "dataset": "Cohere (Medium)", - "db": "Milvus", - "label": "16c64g-sq8-partition_key", - "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 8945.4041, - "latency": 1.9, - "recall": 0.8894, - "filter_ratio": 0.8 + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 1355.3121, + "latency": 4.4, + "recall": 0.9602, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", + "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8-partition_key", - "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 5436.8907, - "latency": 2.0, - "recall": 0.929, - "filter_ratio": 0.5 + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 1079.2123, + "latency": 5.3, + "recall": 0.9648, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 437.1695, - "latency": 5.1, - "recall": 0.951, + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 876.5772, + "latency": 6.3, + "recall": 0.9676, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 400.0053, - "latency": 5.6, - "recall": 0.9558, + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 5973.0024, + "latency": 2.4, + "recall": 0.9192, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 343.938, - "latency": 6.5, - "recall": 0.9605, + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 5416.5758, + "latency": 2.6, + "recall": 0.9334, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 275.2271, - "latency": 7.7, - "recall": 0.9649, + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 4771.4324, + "latency": 2.8, + "recall": 0.9479, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 234.9937, - "latency": 8.7, - "recall": 0.9677, + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 4006.3994, + "latency": 3.2, + "recall": 0.9609, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 202.6975, - "latency": 9.9, - "recall": 0.9696, + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 3441.7597, + "latency": 3.5, + "recall": 0.9682, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 164.6073, - "latency": 12.1, - "recall": 0.972, + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 3040.6216, + "latency": 3.7, + "recall": 0.9734, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 135.9624, - "latency": 13.9, - "recall": 0.9733, + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 2446.7373, + "latency": 4.3, + "recall": 0.9791, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 432.8374, + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "qps": 2084.6245, "latency": 5.0, - "recall": 0.9865, - "filter_ratio": 0.999 + "recall": 0.9819, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 368.6042, - "latency": 5.4, - "recall": 0.9843, - "filter_ratio": 0.998 + "label": "16c64g-sq8-partition_key", + "db_name": "Milvus-16c64g-sq8-partition_key", + "qps": 920.9627, + "latency": 3.4, + "recall": 0.9488, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 318.8159, - "latency": 7.1, - "recall": 0.9836, - "filter_ratio": 0.995 + "label": "16c64g-sq8-partition_key", + "db_name": "Milvus-16c64g-sq8-partition_key", + "qps": 1985.8124, + "latency": 2.6, + "recall": 0.9407, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 217.0963, - "latency": 9.1, - "recall": 0.9822, - "filter_ratio": 0.99 + "label": "16c64g-sq8-partition_key", + "db_name": "Milvus-16c64g-sq8-partition_key", + "qps": 3157.707, + "latency": 2.3, + "recall": 0.9347, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 150.9989, - "latency": 12.5, - "recall": 0.9814, - "filter_ratio": 0.98 + "label": "16c64g-sq8-partition_key", + "db_name": "Milvus-16c64g-sq8-partition_key", + "qps": 5671.2562, + "latency": 2.1, + "recall": 0.903, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 76.0341, - "latency": 23.1, - "recall": 0.9797, - "filter_ratio": 0.95 + "label": "16c64g-sq8-partition_key", + "db_name": "Milvus-16c64g-sq8-partition_key", + "qps": 8936.7962, + "latency": 2.0, + "recall": 0.8835, + "filter_ratio": 0.98 }, { "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 166.5611, - "latency": 11.5, - "recall": 0.9675, - "filter_ratio": 0.9 + "label": "16c64g-sq8-partition_key", + "db_name": "Milvus-16c64g-sq8-partition_key", + "qps": 9664.2855, + "latency": 1.8, + "recall": 0.899, + "filter_ratio": 0.99 }, { "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 248.9625, - "latency": 8.3, - "recall": 0.9608, - "filter_ratio": 0.8 + "label": "16c64g-sq8-partition_key", + "db_name": "Milvus-16c64g-sq8-partition_key", + "qps": 10276.7451, + "latency": 1.7, + "recall": 0.9159, + "filter_ratio": 0.995 }, { "dataset": "Cohere (Large)", "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "qps": 435.3358, - "latency": 8.2, - "recall": 0.9417, - "filter_ratio": 0.5 + "label": "16c64g-sq8-partition_key", + "db_name": "Milvus-16c64g-sq8-partition_key", + "qps": 10891.7531, + "latency": 1.7, + "recall": 0.9408, + "filter_ratio": 0.998 }, { "dataset": "Cohere (Large)", @@ -970,1523 +790,1613 @@ "filter_ratio": 0.999 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", "label": "16c64g-sq8-partition_key", "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 10891.7531, - "latency": 1.7, - "recall": 0.9408, - "filter_ratio": 0.998 + "qps": 5436.8907, + "latency": 2.0, + "recall": 0.929, + "filter_ratio": 0.5 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", "label": "16c64g-sq8-partition_key", "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 10276.7451, - "latency": 1.7, - "recall": 0.9159, - "filter_ratio": 0.995 + "qps": 8945.4041, + "latency": 1.9, + "recall": 0.8894, + "filter_ratio": 0.8 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", "label": "16c64g-sq8-partition_key", "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 9664.2855, - "latency": 1.8, - "recall": 0.899, - "filter_ratio": 0.99 + "qps": 9681.5656, + "latency": 1.9, + "recall": 0.9008, + "filter_ratio": 0.9 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", "label": "16c64g-sq8-partition_key", "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 8936.7962, - "latency": 2.0, - "recall": 0.8835, - "filter_ratio": 0.98 + "qps": 10258.2661, + "latency": 1.7, + "recall": 0.9139, + "filter_ratio": 0.95 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", "label": "16c64g-sq8-partition_key", "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 5671.2562, - "latency": 2.1, - "recall": 0.903, - "filter_ratio": 0.95 + "qps": 10671.8925, + "latency": 1.7, + "recall": 0.9339, + "filter_ratio": 0.98 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", "label": "16c64g-sq8-partition_key", "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 3157.707, - "latency": 2.3, - "recall": 0.9347, - "filter_ratio": 0.9 + "qps": 11280.0849, + "latency": 1.6, + "recall": 0.9507, + "filter_ratio": 0.99 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", "label": "16c64g-sq8-partition_key", "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 1985.8124, - "latency": 2.6, - "recall": 0.9407, - "filter_ratio": 0.8 + "qps": 11520.9234, + "latency": 1.5, + "recall": 0.9634, + "filter_ratio": 0.995 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "Milvus", "label": "16c64g-sq8-partition_key", "db_name": "Milvus-16c64g-sq8-partition_key", - "qps": 920.9627, - "latency": 3.4, - "recall": 0.9488, - "filter_ratio": 0.5 - }, - { - "dataset": "Cohere (Medium)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 1146.5286, - "latency": 13.7, - "recall": 0.9262, - "filter_ratio": 0.0 + "qps": 11803.1944, + "latency": 1.5, + "recall": 0.9778, + "filter_ratio": 0.998 }, { "dataset": "Cohere (Medium)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 1148.1735, - "latency": 8.9, - "recall": 0.9801, + "db": "Milvus", + "label": "16c64g-sq8-partition_key", + "db_name": "Milvus-16c64g-sq8-partition_key", + "qps": 11763.5538, + "latency": 1.5, + "recall": 1.0, "filter_ratio": 0.999 }, { - "dataset": "Cohere (Medium)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 1149.1219, - "latency": 10.3, - "recall": 0.9764, - "filter_ratio": 0.998 + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 505.7458, + "latency": 20.7, + "recall": 0.9068, + "filter_ratio": 0.0 + }, + { + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 433.9034, + "latency": 23.1, + "recall": 0.931, + "filter_ratio": 0.0 + }, + { + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 381.7737, + "latency": 25.7, + "recall": 0.9431, + "filter_ratio": 0.0 + }, + { + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 342.1123, + "latency": 29.0, + "recall": 0.951, + "filter_ratio": 0.0 + }, + { + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 308.2216, + "latency": 31.3, + "recall": 0.9561, + "filter_ratio": 0.0 + }, + { + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 257.7928, + "latency": 36.4, + "recall": 0.9626, + "filter_ratio": 0.0 + }, + { + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 223.8166, + "latency": 42.1, + "recall": 0.9666, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 1140.4099, - "latency": 13.5, - "recall": 0.9716, - "filter_ratio": 0.995 + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 950.6332, + "latency": 13.2, + "recall": 0.914, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 1123.5147, - "latency": 18.5, - "recall": 0.9688, - "filter_ratio": 0.99 + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 823.2224, + "latency": 13.5, + "recall": 0.9434, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 487.8343, - "latency": 25.4, - "recall": 0.9668, - "filter_ratio": 0.98 + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 743.9815, + "latency": 14.8, + "recall": 0.9583, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 264.9324, - "latency": 49.6, - "recall": 0.936, - "filter_ratio": 0.95 + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 683.1873, + "latency": 15.7, + "recall": 0.9677, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 492.4887, - "latency": 29.6, - "recall": 0.9269, - "filter_ratio": 0.9 + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 619.7468, + "latency": 17.2, + "recall": 0.9738, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 823.1775, - "latency": 20.5, - "recall": 0.9148, - "filter_ratio": 0.8 + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 537.4082, + "latency": 18.8, + "recall": 0.9809, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 1147.1977, - "latency": 13.3, - "recall": 0.8999, - "filter_ratio": 0.5 + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "qps": 474.9941, + "latency": 20.9, + "recall": 0.9848, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 1131.3087, - "latency": 14.1, - "recall": 0.9024, + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 1610.9496, + "latency": 10.8, + "recall": 0.9, "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 1114.952, - "latency": 12.7, - "recall": 0.97, - "filter_ratio": 0.999 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 1557.3623, + "latency": 10.8, + "recall": 0.9244, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 583.5009, - "latency": 23.0, - "recall": 0.9668, - "filter_ratio": 0.998 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 1473.9256, + "latency": 11.7, + "recall": 0.9484, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 31.4779, - "latency": 351.0, - "recall": 0.9414, - "filter_ratio": 0.995 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 1388.5547, + "latency": 12.5, + "recall": 0.9597, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 57.8988, - "latency": 200.1, - "recall": 0.9332, - "filter_ratio": 0.99 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 8.0584, + "latency": 1757.9, + "recall": 1.0, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Large)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 101.1774, - "latency": 116.1, - "recall": 0.9241, - "filter_ratio": 0.98 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 808.3294, + "latency": 22.2, + "recall": 0.7016, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Large)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 212.7466, - "latency": 58.7, - "recall": 0.9099, - "filter_ratio": 0.95 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 1053.1495, + "latency": 17.7, + "recall": 0.5673, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Large)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 372.2462, - "latency": 35.9, - "recall": 0.8977, - "filter_ratio": 0.9 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 504.9179, + "latency": 272.6, + "recall": 0.4664, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Large)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 617.0881, - "latency": 22.4, - "recall": 0.8844, - "filter_ratio": 0.8 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 114.8061, + "latency": 126.6, + "recall": 0.9985, + "filter_ratio": 0.98 }, { "dataset": "Cohere (Large)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "qps": 1094.5967, - "latency": 14.3, - "recall": 0.8659, - "filter_ratio": 0.5 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 210.3227, + "latency": 71.4, + "recall": 1.0, + "filter_ratio": 0.99 }, { - "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 4318.9697, - "latency": 4.3, + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 353.7862, + "latency": 45.2, "recall": 1.0, - "filter_ratio": 0.999 + "filter_ratio": 0.995 }, { - "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 4250.2894, - "latency": 4.6, - "recall": 1.0, + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 696.9777, + "latency": 24.6, + "recall": 0.997, "filter_ratio": 0.998 }, + { + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 1022.2696, + "latency": 17.9, + "recall": 0.936, + "filter_ratio": 0.999 + }, { "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 2997.4391, - "latency": 6.1, - "recall": 1.0, - "filter_ratio": 0.995 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 3055.0123, + "latency": 7.2, + "recall": 0.9066, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 1494.5334, - "latency": 7.0, - "recall": 1.0, - "filter_ratio": 0.99 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 3013.4439, + "latency": 6.9, + "recall": 0.9268, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 1108.6473, + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 2801.7241, "latency": 7.4, - "recall": 0.995, - "filter_ratio": 0.98 + "recall": 0.9476, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 1289.5164, - "latency": 6.4, - "recall": 0.9906, - "filter_ratio": 0.95 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 2590.3809, + "latency": 8.6, + "recall": 0.9679, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 1059.3394, - "latency": 7.8, - "recall": 0.9856, - "filter_ratio": 0.9 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 2291.2159, + "latency": 8.9, + "recall": 0.9764, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 987.0795, - "latency": 7.1, - "recall": 0.9804, - "filter_ratio": 0.8 + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 2159.051, + "latency": 9.4, + "recall": 0.801, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 1591.7055, + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 2604.4444, "latency": 7.8, - "recall": 0.8506, - "filter_ratio": 0.5 + "recall": 0.63, + "filter_ratio": 0.8 }, { - "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 1202.8677, - "latency": 7.0, - "recall": 1.0, - "filter_ratio": 0.999 + "dataset": "Cohere (Medium)", + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 2685.6654, + "latency": 7.6, + "recall": 0.4914, + "filter_ratio": 0.9 }, { - "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 639.3991, - "latency": 7.3, - "recall": 1.0, - "filter_ratio": 0.998 + "dataset": "Cohere (Medium)", + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 677.1414, + "latency": 33.5, + "recall": 0.7655, + "filter_ratio": 0.95 }, { - "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 274.8559, - "latency": 9.9, + "dataset": "Cohere (Medium)", + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 942.2296, + "latency": 18.2, "recall": 1.0, - "filter_ratio": 0.995 + "filter_ratio": 0.98 }, { - "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 441.4152, - "latency": 8.3, - "recall": 0.997, + "dataset": "Cohere (Medium)", + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 1507.6899, + "latency": 12.8, + "recall": 1.0, "filter_ratio": 0.99 }, { - "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 358.8949, - "latency": 9.5, - "recall": 0.995, - "filter_ratio": 0.98 - }, + "dataset": "Cohere (Medium)", + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 2073.2153, + "latency": 11.0, + "recall": 1.0, + "filter_ratio": 0.995 + }, { - "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 325.2245, - "latency": 10.3, - "recall": 0.9909, - "filter_ratio": 0.95 + "dataset": "Cohere (Medium)", + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 3014.2483, + "latency": 7.0, + "recall": 1.0, + "filter_ratio": 0.998 + }, + { + "dataset": "Cohere (Medium)", + "db": "OpenSearch", + "label": "16c128g-force_merge", + "db_name": "OpenSearch-16c128g-force_merge", + "qps": 3099.4124, + "latency": 6.2, + "recall": 1.0, + "filter_ratio": 0.999 }, { "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 273.4174, - "latency": 13.3, - "recall": 0.9789, - "filter_ratio": 0.9 + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 13.0379, + "latency": 1063.5, + "recall": 1.0, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 262.8314, - "latency": 11.3, - "recall": 0.9808, + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 1301.4102, + "latency": 14.7, + "recall": 0.9011, "filter_ratio": 0.8 }, { "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g-payload-index", - "db_name": "QdrantCloud-16c64g-payload-index", - "qps": 434.5481, - "latency": 8.5, - "recall": 0.7237, - "filter_ratio": 0.5 + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 1844.8918, + "latency": 10.6, + "recall": 0.9085, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g", - "db_name": "QdrantCloud-16c64g", - "qps": 446.9116, - "latency": 9.2, - "recall": 0.9357, - "filter_ratio": 0.0 + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 2275.2854, + "latency": 9.1, + "recall": 0.917, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g", - "db_name": "QdrantCloud-16c64g", - "qps": 388.3028, - "latency": 9.6, - "recall": 0.9431, - "filter_ratio": 0.0 + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 2708.6752, + "latency": 8.4, + "recall": 0.9337, + "filter_ratio": 0.98 }, { "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g", - "db_name": "QdrantCloud-16c64g", - "qps": 323.3964, - "latency": 9.8, - "recall": 0.9507, - "filter_ratio": 0.0 + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 2782.0274, + "latency": 7.4, + "recall": 0.9466, + "filter_ratio": 0.99 }, { "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g", - "db_name": "QdrantCloud-16c64g", - "qps": 256.4668, - "latency": 11.3, - "recall": 0.9588, - "filter_ratio": 0.0 + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 2950.717, + "latency": 6.9, + "recall": 0.9558, + "filter_ratio": 0.995 }, { "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g", - "db_name": "QdrantCloud-16c64g", - "qps": 145.5316, - "latency": 18.4, - "recall": 0.9726, - "filter_ratio": 0.0 + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 2988.4205, + "latency": 7.6, + "recall": 0.9741, + "filter_ratio": 0.998 }, { - "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g", - "db_name": "QdrantCloud-16c64g", - "qps": 1242.428, + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 3033.5491, "latency": 6.4, - "recall": 0.9474, - "filter_ratio": 0.0 + "recall": 0.9844, + "filter_ratio": 0.999 }, { "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g", - "db_name": "QdrantCloud-16c64g", - "qps": 1111.3633, - "latency": 7.0, - "recall": 0.955, - "filter_ratio": 0.0 + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 2251.1274, + "latency": 8.7, + "recall": 0.8848, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g", - "db_name": "QdrantCloud-16c64g", - "qps": 955.4701, - "latency": 7.2, - "recall": 0.9629, - "filter_ratio": 0.0 + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 2771.2009, + "latency": 7.6, + "recall": 0.889, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g", - "db_name": "QdrantCloud-16c64g", - "qps": 783.5207, - "latency": 7.7, - "recall": 0.971, - "filter_ratio": 0.0 + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 2935.9403, + "latency": 6.8, + "recall": 0.8992, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Medium)", - "db": "QdrantCloud", - "label": "16c64g", - "db_name": "QdrantCloud-16c64g", - "qps": 470.8546, - "latency": 9.5, - "recall": 0.9835, - "filter_ratio": 0.0 + "db": "OpenSearch", + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 3028.858, + "latency": 6.7, + "recall": 0.9133, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Medium)", "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 950.6332, - "latency": 13.2, - "recall": 0.914, - "filter_ratio": 0.0 + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 3065.6134, + "latency": 6.2, + "recall": 0.9328, + "filter_ratio": 0.98 }, { "dataset": "Cohere (Medium)", "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 823.2224, - "latency": 13.5, - "recall": 0.9434, - "filter_ratio": 0.0 + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 3064.6288, + "latency": 6.5, + "recall": 0.9507, + "filter_ratio": 0.99 }, { "dataset": "Cohere (Medium)", "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 743.9815, - "latency": 14.8, - "recall": 0.9583, - "filter_ratio": 0.0 + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 3090.0478, + "latency": 6.4, + "recall": 0.9628, + "filter_ratio": 0.995 }, { "dataset": "Cohere (Medium)", "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 683.1873, - "latency": 15.7, - "recall": 0.9677, - "filter_ratio": 0.0 + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 3086.1957, + "latency": 6.7, + "recall": 1.0, + "filter_ratio": 0.998 }, { "dataset": "Cohere (Medium)", "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 619.7468, - "latency": 17.2, - "recall": 0.9738, - "filter_ratio": 0.0 + "label": "16c128g-routing-64shard-force_merge", + "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", + "qps": 3103.0539, + "latency": 5.6, + "recall": 1.0, + "filter_ratio": 0.999 }, { - "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 537.4082, - "latency": 18.8, - "recall": 0.9809, + "dataset": "Cohere (Large)", + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 1131.3087, + "latency": 14.1, + "recall": 0.9024, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 474.9941, - "latency": 20.9, - "recall": 0.9848, - "filter_ratio": 0.0 + "dataset": "Cohere (Large)", + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 1094.5967, + "latency": 14.3, + "recall": 0.8659, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 505.7458, - "latency": 20.7, - "recall": 0.9068, - "filter_ratio": 0.0 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 617.0881, + "latency": 22.4, + "recall": 0.8844, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 433.9034, - "latency": 23.1, - "recall": 0.931, - "filter_ratio": 0.0 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 372.2462, + "latency": 35.9, + "recall": 0.8977, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 381.7737, - "latency": 25.7, - "recall": 0.9431, - "filter_ratio": 0.0 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 212.7466, + "latency": 58.7, + "recall": 0.9099, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 342.1123, - "latency": 29.0, - "recall": 0.951, - "filter_ratio": 0.0 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 101.1774, + "latency": 116.1, + "recall": 0.9241, + "filter_ratio": 0.98 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 308.2216, - "latency": 31.3, - "recall": 0.9561, - "filter_ratio": 0.0 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 57.8988, + "latency": 200.1, + "recall": 0.9332, + "filter_ratio": 0.99 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 257.7928, - "latency": 36.4, - "recall": 0.9626, - "filter_ratio": 0.0 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 31.4779, + "latency": 351.0, + "recall": 0.9414, + "filter_ratio": 0.995 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "qps": 223.8166, - "latency": 42.1, - "recall": 0.9666, - "filter_ratio": 0.0 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 583.5009, + "latency": 23.0, + "recall": 0.9668, + "filter_ratio": 0.998 + }, + { + "dataset": "Cohere (Large)", + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 1114.952, + "latency": 12.7, + "recall": 0.97, + "filter_ratio": 0.999 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 3055.0123, - "latency": 7.2, - "recall": 0.9066, + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 1146.5286, + "latency": 13.7, + "recall": 0.9262, "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 3013.4439, - "latency": 6.9, - "recall": 0.9268, - "filter_ratio": 0.0 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 1147.1977, + "latency": 13.3, + "recall": 0.8999, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 2801.7241, - "latency": 7.4, - "recall": 0.9476, - "filter_ratio": 0.0 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 823.1775, + "latency": 20.5, + "recall": 0.9148, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 2590.3809, - "latency": 8.6, - "recall": 0.9679, - "filter_ratio": 0.0 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 492.4887, + "latency": 29.6, + "recall": 0.9269, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 2291.2159, - "latency": 8.9, - "recall": 0.9764, - "filter_ratio": 0.0 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 264.9324, + "latency": 49.6, + "recall": 0.936, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 3099.4124, - "latency": 6.2, - "recall": 1.0, - "filter_ratio": 0.999 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 487.8343, + "latency": 25.4, + "recall": 0.9668, + "filter_ratio": 0.98 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 3014.2483, - "latency": 7.0, - "recall": 1.0, - "filter_ratio": 0.998 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 1123.5147, + "latency": 18.5, + "recall": 0.9688, + "filter_ratio": 0.99 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 2073.2153, - "latency": 11.0, - "recall": 1.0, + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 1140.4099, + "latency": 13.5, + "recall": 0.9716, "filter_ratio": 0.995 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 1507.6899, - "latency": 12.8, - "recall": 1.0, - "filter_ratio": 0.99 - }, - { - "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 942.2296, - "latency": 18.2, - "recall": 1.0, - "filter_ratio": 0.98 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 1149.1219, + "latency": 10.3, + "recall": 0.9764, + "filter_ratio": 0.998 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 677.1414, - "latency": 33.5, - "recall": 0.7655, - "filter_ratio": 0.95 + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "qps": 1148.1735, + "latency": 8.9, + "recall": 0.9801, + "filter_ratio": 0.999 }, { - "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 2685.6654, - "latency": 7.6, - "recall": 0.4914, - "filter_ratio": 0.9 + "dataset": "Cohere (Large)", + "db": "QdrantCloud", + "label": "16c64g", + "db_name": "QdrantCloud-16c64g", + "qps": 446.9116, + "latency": 9.2, + "recall": 0.9357, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 2604.4444, - "latency": 7.8, - "recall": 0.63, - "filter_ratio": 0.8 + "dataset": "Cohere (Large)", + "db": "QdrantCloud", + "label": "16c64g", + "db_name": "QdrantCloud-16c64g", + "qps": 388.3028, + "latency": 9.6, + "recall": 0.9431, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 2159.051, - "latency": 9.4, - "recall": 0.801, - "filter_ratio": 0.5 + "dataset": "Cohere (Large)", + "db": "QdrantCloud", + "label": "16c64g", + "db_name": "QdrantCloud-16c64g", + "qps": 323.3964, + "latency": 9.8, + "recall": 0.9507, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 2251.1274, - "latency": 8.7, - "recall": 0.8848, - "filter_ratio": 0.5 + "dataset": "Cohere (Large)", + "db": "QdrantCloud", + "label": "16c64g", + "db_name": "QdrantCloud-16c64g", + "qps": 256.4668, + "latency": 11.3, + "recall": 0.9588, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 3103.0539, - "latency": 5.6, - "recall": 1.0, - "filter_ratio": 0.999 + "dataset": "Cohere (Large)", + "db": "QdrantCloud", + "label": "16c64g", + "db_name": "QdrantCloud-16c64g", + "qps": 145.5316, + "latency": 18.4, + "recall": 0.9726, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 3086.1957, - "latency": 6.7, - "recall": 1.0, - "filter_ratio": 0.998 + "db": "QdrantCloud", + "label": "16c64g", + "db_name": "QdrantCloud-16c64g", + "qps": 1242.428, + "latency": 6.4, + "recall": 0.9474, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 3090.0478, - "latency": 6.4, - "recall": 0.9628, - "filter_ratio": 0.995 + "db": "QdrantCloud", + "label": "16c64g", + "db_name": "QdrantCloud-16c64g", + "qps": 1111.3633, + "latency": 7.0, + "recall": 0.955, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 3064.6288, - "latency": 6.5, - "recall": 0.9507, - "filter_ratio": 0.99 + "db": "QdrantCloud", + "label": "16c64g", + "db_name": "QdrantCloud-16c64g", + "qps": 955.4701, + "latency": 7.2, + "recall": 0.9629, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 3065.6134, - "latency": 6.2, - "recall": 0.9328, - "filter_ratio": 0.98 + "db": "QdrantCloud", + "label": "16c64g", + "db_name": "QdrantCloud-16c64g", + "qps": 783.5207, + "latency": 7.7, + "recall": 0.971, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 3028.858, - "latency": 6.7, - "recall": 0.9133, - "filter_ratio": 0.95 + "db": "QdrantCloud", + "label": "16c64g", + "db_name": "QdrantCloud-16c64g", + "qps": 470.8546, + "latency": 9.5, + "recall": 0.9835, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 2935.9403, - "latency": 6.8, - "recall": 0.8992, - "filter_ratio": 0.9 + "dataset": "Cohere (Large)", + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 434.5481, + "latency": 8.5, + "recall": 0.7237, + "filter_ratio": 0.5 }, { - "dataset": "Cohere (Medium)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 2771.2009, - "latency": 7.6, - "recall": 0.889, + "dataset": "Cohere (Large)", + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 262.8314, + "latency": 11.3, + "recall": 0.9808, "filter_ratio": 0.8 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 1610.9496, - "latency": 10.8, - "recall": 0.9, - "filter_ratio": 0.0 + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 273.4174, + "latency": 13.3, + "recall": 0.9789, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 1557.3623, - "latency": 10.8, - "recall": 0.9244, - "filter_ratio": 0.0 + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 325.2245, + "latency": 10.3, + "recall": 0.9909, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 1473.9256, - "latency": 11.7, - "recall": 0.9484, - "filter_ratio": 0.0 + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 358.8949, + "latency": 9.5, + "recall": 0.995, + "filter_ratio": 0.98 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 1388.5547, - "latency": 12.5, - "recall": 0.9597, - "filter_ratio": 0.0 + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 441.4152, + "latency": 8.3, + "recall": 0.997, + "filter_ratio": 0.99 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 1022.2696, - "latency": 17.9, - "recall": 0.936, - "filter_ratio": 0.999 + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 274.8559, + "latency": 9.9, + "recall": 1.0, + "filter_ratio": 0.995 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 696.9777, - "latency": 24.6, - "recall": 0.997, + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 639.3991, + "latency": 7.3, + "recall": 1.0, "filter_ratio": 0.998 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 353.7862, - "latency": 45.2, + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 1202.8677, + "latency": 7.0, "recall": 1.0, - "filter_ratio": 0.995 + "filter_ratio": 0.999 }, { - "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 210.3227, - "latency": 71.4, - "recall": 1.0, - "filter_ratio": 0.99 + "dataset": "Cohere (Medium)", + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 1591.7055, + "latency": 7.8, + "recall": 0.8506, + "filter_ratio": 0.5 }, { - "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 114.8061, - "latency": 126.6, - "recall": 0.9985, - "filter_ratio": 0.98 + "dataset": "Cohere (Medium)", + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 987.0795, + "latency": 7.1, + "recall": 0.9804, + "filter_ratio": 0.8 }, { - "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 504.9179, - "latency": 272.6, - "recall": 0.4664, - "filter_ratio": 0.95 + "dataset": "Cohere (Medium)", + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 1059.3394, + "latency": 7.8, + "recall": 0.9856, + "filter_ratio": 0.9 }, { - "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 1053.1495, - "latency": 17.7, - "recall": 0.5673, - "filter_ratio": 0.9 + "dataset": "Cohere (Medium)", + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 1289.5164, + "latency": 6.4, + "recall": 0.9906, + "filter_ratio": 0.95 }, { - "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 808.3294, - "latency": 22.2, - "recall": 0.7016, - "filter_ratio": 0.8 + "dataset": "Cohere (Medium)", + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 1108.6473, + "latency": 7.4, + "recall": 0.995, + "filter_ratio": 0.98 }, { - "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-force_merge", - "db_name": "OpenSearch-16c128g-force_merge", - "qps": 8.0584, - "latency": 1757.9, + "dataset": "Cohere (Medium)", + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 1494.5334, + "latency": 7.0, "recall": 1.0, - "filter_ratio": 0.5 + "filter_ratio": 0.99 }, { - "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 3033.5491, - "latency": 6.4, - "recall": 0.9844, - "filter_ratio": 0.999 + "dataset": "Cohere (Medium)", + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 2997.4391, + "latency": 6.1, + "recall": 1.0, + "filter_ratio": 0.995 }, { - "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 2988.4205, - "latency": 7.6, - "recall": 0.9741, + "dataset": "Cohere (Medium)", + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 4250.2894, + "latency": 4.6, + "recall": 1.0, "filter_ratio": 0.998 }, + { + "dataset": "Cohere (Medium)", + "db": "QdrantCloud", + "label": "16c64g-payload-index", + "db_name": "QdrantCloud-16c64g-payload-index", + "qps": 4318.9697, + "latency": 4.3, + "recall": 1.0, + "filter_ratio": 0.999 + }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 2950.717, - "latency": 6.9, - "recall": 0.9558, - "filter_ratio": 0.995 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 194.8021, + "latency": 559.8, + "recall": 0.86, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 2782.0274, - "latency": 7.4, - "recall": 0.9466, - "filter_ratio": 0.99 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 199.5444, + "latency": 463.9, + "recall": 0.8478, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 2708.6752, - "latency": 8.4, - "recall": 0.9337, - "filter_ratio": 0.98 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 179.4203, + "latency": 497.5, + "recall": 0.8273, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 2275.2854, - "latency": 9.1, - "recall": 0.917, - "filter_ratio": 0.95 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 192.1458, + "latency": 480.5, + "recall": 0.8103, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 1844.8918, - "latency": 10.6, - "recall": 0.9085, - "filter_ratio": 0.9 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 186.0237, + "latency": 474.0, + "recall": 0.7847, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 1301.4102, - "latency": 14.7, - "recall": 0.9011, - "filter_ratio": 0.8 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 190.9747, + "latency": 517.4, + "recall": 0.7398, + "filter_ratio": 0.98 }, { "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g-routing-64shard-force_merge", - "db_name": "OpenSearch-16c128g-routing-64shard-force_merge", - "qps": 13.0379, - "latency": 1063.5, - "recall": 1.0, - "filter_ratio": 0.5 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 172.95, + "latency": 515.6, + "recall": 0.7004, + "filter_ratio": 0.99 }, { - "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 9704.4214, - "latency": 2.5, - "recall": 0.9169, - "filter_ratio": 0.0 + "dataset": "Cohere (Large)", + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 174.3549, + "latency": 496.9, + "recall": 0.6279, + "filter_ratio": 0.995 }, { - "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 9463.4991, - "latency": 2.6, - "recall": 0.9393, - "filter_ratio": 0.0 + "dataset": "Cohere (Large)", + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 198.397, + "latency": 506.9, + "recall": 0.5409, + "filter_ratio": 0.998 + }, + { + "dataset": "Cohere (Large)", + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 187.4268, + "latency": 453.7, + "recall": 0.4692, + "filter_ratio": 0.999 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 9194.9922, - "latency": 2.7, - "recall": 0.9543, + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 199.4972, + "latency": 337.1, + "recall": 0.8717, "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 8779.8779, - "latency": 2.9, - "recall": 0.9685, - "filter_ratio": 0.0 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 201.1282, + "latency": 269.2, + "recall": 0.8637, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 8153.4648, - "latency": 3.0, - "recall": 0.9757, - "filter_ratio": 0.0 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 202.1405, + "latency": 282.6, + "recall": 0.8492, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 6848.5254, - "latency": 4.4, - "recall": 0.9835, - "filter_ratio": 0.0 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 199.0349, + "latency": 275.3, + "recall": 0.8325, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 6124.4431, - "latency": 3.8, - "recall": 0.9873, - "filter_ratio": 0.0 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 198.599, + "latency": 358.8, + "recall": 0.8085, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 5186.8135, - "latency": 5.3, - "recall": 0.9893, - "filter_ratio": 0.0 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 202.2424, + "latency": 301.7, + "recall": 0.7592, + "filter_ratio": 0.98 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 4898.2048, - "latency": 5.6, - "recall": 0.9904, - "filter_ratio": 0.0 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 201.5401, + "latency": 282.4, + "recall": 0.7086, + "filter_ratio": 0.99 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 9773.6593, - "latency": 3.7, - "recall": 0.9955, - "filter_ratio": 0.999 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 196.9391, + "latency": 263.4, + "recall": 0.6549, + "filter_ratio": 0.995 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 9081.1518, - "latency": 3.0, - "recall": 0.9943, + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 197.4455, + "latency": 349.3, + "recall": 0.5314, "filter_ratio": 0.998 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 8455.2896, - "latency": 4.0, - "recall": 0.9921, - "filter_ratio": 0.995 + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "qps": 192.1164, + "latency": 345.6, + "recall": 0.4276, + "filter_ratio": 0.999 }, { - "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 7610.0519, - "latency": 3.3, - "recall": 0.9903, - "filter_ratio": 0.99 + "dataset": "Cohere (Large)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 649.8781, + "latency": 55.2, + "recall": 0.8352, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 7589.664, - "latency": 3.8, - "recall": 0.9235, - "filter_ratio": 0.98 + "dataset": "Cohere (Large)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 351.7114, + "latency": 46.7, + "recall": 0.8735, + "filter_ratio": 0.5 }, { - "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 6750.2495, - "latency": 4.4, - "recall": 0.9105, - "filter_ratio": 0.95 + "dataset": "Cohere (Large)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 365.2505, + "latency": 34.9, + "recall": 0.8251, + "filter_ratio": 0.8 }, { - "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 5506.1808, - "latency": 5.5, - "recall": 0.9193, + "dataset": "Cohere (Large)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 369.4921, + "latency": 41.6, + "recall": 0.779, "filter_ratio": 0.9 }, { - "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 6860.8577, - "latency": 4.7, - "recall": 0.9226, - "filter_ratio": 0.8 + "dataset": "Cohere (Large)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 370.7241, + "latency": 49.6, + "recall": 0.7177, + "filter_ratio": 0.95 }, { - "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "qps": 8468.4611, - "latency": 3.1, - "recall": 0.8925, - "filter_ratio": 0.5 + "dataset": "Cohere (Large)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 382.5332, + "latency": 54.7, + "recall": 0.6135, + "filter_ratio": 0.98 }, { - "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 10089.4308, - "latency": 2.6, - "recall": 0.9934, - "filter_ratio": 0.999 + "dataset": "Cohere (Large)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 81.8678, + "latency": 105.6, + "recall": 0.8751, + "filter_ratio": 0.99 }, { - "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 10557.4373, - "latency": 2.7, - "recall": 0.9393, + "dataset": "Cohere (Large)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 91.8612, + "latency": 85.7, + "recall": 0.8799, + "filter_ratio": 0.995 + }, + { + "dataset": "Cohere (Large)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 96.592, + "latency": 76.9, + "recall": 0.9178, "filter_ratio": 0.998 }, { - "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 9805.0401, - "latency": 2.6, - "recall": 0.9257, - "filter_ratio": 0.995 + "dataset": "Cohere (Large)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 100.0554, + "latency": 69.3, + "recall": 0.9638, + "filter_ratio": 0.999 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 10020.5299, - "latency": 2.6, - "recall": 0.9788, - "filter_ratio": 0.99 + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 798.328, + "latency": 56.7, + "recall": 0.8993, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 10041.0338, - "latency": 2.7, - "recall": 0.9693, - "filter_ratio": 0.98 + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 802.6923, + "latency": 48.1, + "recall": 0.935, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 9861.9686, - "latency": 2.6, - "recall": 0.955, - "filter_ratio": 0.95 + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 310.957, + "latency": 49.4, + "recall": 0.9698, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 9507.9991, - "latency": 2.8, - "recall": 0.9453, + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 260.4031, + "latency": 48.3, + "recall": 0.9828, "filter_ratio": 0.9 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 9428.4531, - "latency": 2.6, - "recall": 0.9331, - "filter_ratio": 0.8 + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 206.0934, + "latency": 56.7, + "recall": 0.9795, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Medium)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 9048.6431, - "latency": 3.9, - "recall": 0.9216, - "filter_ratio": 0.5 - }, - { - "dataset": "Cohere (Large)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 8695.2765, - "latency": 4.3, - "recall": 0.9603, - "filter_ratio": 0.999 + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 184.5363, + "latency": 53.4, + "recall": 0.9681, + "filter_ratio": 0.98 }, { - "dataset": "Cohere (Large)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 9244.1135, - "latency": 4.2, - "recall": 0.9724, - "filter_ratio": 0.998 + "dataset": "Cohere (Medium)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 346.5847, + "latency": 42.7, + "recall": 0.9631, + "filter_ratio": 0.99 }, { - "dataset": "Cohere (Large)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 9289.0118, - "latency": 4.2, - "recall": 0.9574, + "dataset": "Cohere (Medium)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 284.6367, + "latency": 47.6, + "recall": 0.9788, "filter_ratio": 0.995 }, { - "dataset": "Cohere (Large)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 9374.8941, - "latency": 4.2, - "recall": 0.9425, - "filter_ratio": 0.99 + "dataset": "Cohere (Medium)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 323.0238, + "latency": 50.4, + "recall": 1.0, + "filter_ratio": 0.998 }, { - "dataset": "Cohere (Large)", - "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 9368.1325, - "latency": 3.8, - "recall": 0.9292, - "filter_ratio": 0.98 + "dataset": "Cohere (Medium)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "qps": 471.553, + "latency": 44.1, + "recall": 1.0, + "filter_ratio": 0.999 }, { "dataset": "Cohere (Large)", "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 9220.3627, - "latency": 3.8, - "recall": 0.9081, - "filter_ratio": 0.95 + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 7385.2066, + "latency": 2.1, + "recall": 0.9384, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 8633.8949, - "latency": 4.1, - "recall": 0.8928, - "filter_ratio": 0.9 + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 6793.8443, + "latency": 2.2, + "recall": 0.9522, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 6820.6863, - "latency": 3.2, - "recall": 0.9159, - "filter_ratio": 0.8 + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 6242.5346, + "latency": 2.3, + "recall": 0.961, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", "db": "ZillizCloud", - "label": "8cu-perf-partition_key", - "db_name": "ZillizCloud-8cu-perf-partition_key", - "qps": 3938.6004, - "latency": 3.7, - "recall": 0.9196, - "filter_ratio": 0.5 + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 5779.119, + "latency": 2.3, + "recall": 0.971, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 3957.0757, - "latency": 2.7, - "recall": 0.932, + "qps": 5183.5843, + "latency": 2.4, + "recall": 0.9784, "filter_ratio": 0.0 }, { @@ -2494,9 +2404,9 @@ "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 3539.2869, - "latency": 4.3, - "recall": 0.9471, + "qps": 4259.5572, + "latency": 2.6, + "recall": 0.9845, "filter_ratio": 0.0 }, { @@ -2504,9 +2414,9 @@ "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 3154.6501, - "latency": 3.9, - "recall": 0.9565, + "qps": 3614.3118, + "latency": 2.9, + "recall": 0.9877, "filter_ratio": 0.0 }, { @@ -2514,9 +2424,9 @@ "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 2743.4561, - "latency": 4.3, - "recall": 0.9681, + "qps": 3181.0537, + "latency": 3.0, + "recall": 0.9892, "filter_ratio": 0.0 }, { @@ -2524,110 +2434,120 @@ "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 2318.7835, - "latency": 3.1, - "recall": 0.9763, + "qps": 2804.8357, + "latency": 3.3, + "recall": 0.9903, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 1763.2054, - "latency": 5.0, - "recall": 0.9829, + "qps": 13316.2336, + "latency": 2.0, + "recall": 0.9383, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 1454.0462, - "latency": 4.0, - "recall": 0.9863, + "qps": 12837.5287, + "latency": 2.1, + "recall": 0.9588, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 1251.1255, - "latency": 4.5, - "recall": 0.9884, + "qps": 12248.9154, + "latency": 2.2, + "recall": 0.9687, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 1076.8329, - "latency": 4.4, - "recall": 0.9897, + "qps": 11501.6652, + "latency": 2.2, + "recall": 0.9785, "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 3411.0934, - "latency": 3.3, - "recall": 0.995, - "filter_ratio": 0.999 + "qps": 10566.6823, + "latency": 2.4, + "recall": 0.9838, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 2838.356, - "latency": 3.8, - "recall": 0.9946, - "filter_ratio": 0.998 + "qps": 9227.318, + "latency": 2.7, + "recall": 0.9893, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 1826.0672, - "latency": 5.3, - "recall": 0.9938, - "filter_ratio": 0.995 + "qps": 8320.4606, + "latency": 2.9, + "recall": 0.9919, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 1234.6534, - "latency": 6.4, - "recall": 0.9942, - "filter_ratio": 0.99 + "qps": 7524.9879, + "latency": 3.2, + "recall": 0.9931, + "filter_ratio": 0.0 }, { - "dataset": "Cohere (Large)", + "dataset": "Cohere (Medium)", "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 1773.0919, - "latency": 5.3, - "recall": 0.9699, - "filter_ratio": 0.98 + "qps": 6813.2439, + "latency": 3.5, + "recall": 0.9939, + "filter_ratio": 0.0 }, { "dataset": "Cohere (Large)", "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 1454.8382, - "latency": 4.6, - "recall": 0.9659, - "filter_ratio": 0.95 + "qps": 2950.8165, + "latency": 3.3, + "recall": 0.9147, + "filter_ratio": 0.5 + }, + { + "dataset": "Cohere (Large)", + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 2039.8673, + "latency": 3.8, + "recall": 0.9559, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Large)", @@ -2644,219 +2564,329 @@ "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 2039.8673, + "qps": 1454.8382, + "latency": 4.6, + "recall": 0.9659, + "filter_ratio": 0.95 + }, + { + "dataset": "Cohere (Large)", + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 1773.0919, + "latency": 5.3, + "recall": 0.9699, + "filter_ratio": 0.98 + }, + { + "dataset": "Cohere (Large)", + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 1234.6534, + "latency": 6.4, + "recall": 0.9942, + "filter_ratio": 0.99 + }, + { + "dataset": "Cohere (Large)", + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 1826.0672, + "latency": 5.3, + "recall": 0.9938, + "filter_ratio": 0.995 + }, + { + "dataset": "Cohere (Large)", + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 2838.356, "latency": 3.8, - "recall": 0.9559, - "filter_ratio": 0.8 + "recall": 0.9946, + "filter_ratio": 0.998 }, { "dataset": "Cohere (Large)", "db": "ZillizCloud", "label": "8cu-perf", "db_name": "ZillizCloud-8cu-perf", - "qps": 2950.8165, + "qps": 3411.0934, "latency": 3.3, - "recall": 0.9147, - "filter_ratio": 0.5 + "recall": 0.995, + "filter_ratio": 0.999 }, { "dataset": "Cohere (Medium)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 199.4972, - "latency": 337.1, - "recall": 0.8717, - "filter_ratio": 0.0 + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 8468.4611, + "latency": 3.1, + "recall": 0.8925, + "filter_ratio": 0.5 }, { "dataset": "Cohere (Medium)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 192.1164, - "latency": 345.6, - "recall": 0.4276, - "filter_ratio": 0.999 + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 6860.8577, + "latency": 4.7, + "recall": 0.9226, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Medium)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 197.4455, - "latency": 349.3, - "recall": 0.5314, - "filter_ratio": 0.998 + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 5506.1808, + "latency": 5.5, + "recall": 0.9193, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Medium)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 196.9391, - "latency": 263.4, - "recall": 0.6549, - "filter_ratio": 0.995 + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 6750.2495, + "latency": 4.4, + "recall": 0.9105, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Medium)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 201.5401, - "latency": 282.4, - "recall": 0.7086, - "filter_ratio": 0.99 + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 7589.664, + "latency": 3.8, + "recall": 0.9235, + "filter_ratio": 0.98 }, { "dataset": "Cohere (Medium)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 202.2424, - "latency": 301.7, - "recall": 0.7592, - "filter_ratio": 0.98 + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 7610.0519, + "latency": 3.3, + "recall": 0.9903, + "filter_ratio": 0.99 }, { "dataset": "Cohere (Medium)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 198.599, - "latency": 358.8, - "recall": 0.8085, - "filter_ratio": 0.95 + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 8455.2896, + "latency": 4.0, + "recall": 0.9921, + "filter_ratio": 0.995 }, { "dataset": "Cohere (Medium)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 199.0349, - "latency": 275.3, - "recall": 0.8325, - "filter_ratio": 0.9 + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 9081.1518, + "latency": 3.0, + "recall": 0.9943, + "filter_ratio": 0.998 }, { "dataset": "Cohere (Medium)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 202.1405, - "latency": 282.6, - "recall": 0.8492, - "filter_ratio": 0.8 + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "qps": 9773.6593, + "latency": 3.7, + "recall": 0.9955, + "filter_ratio": 0.999 }, { - "dataset": "Cohere (Medium)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 201.1282, - "latency": 269.2, - "recall": 0.8637, + "dataset": "Cohere (Large)", + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 3938.6004, + "latency": 3.7, + "recall": 0.9196, "filter_ratio": 0.5 }, { "dataset": "Cohere (Large)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 194.8021, - "latency": 559.8, - "recall": 0.86, - "filter_ratio": 0.0 + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 6820.6863, + "latency": 3.2, + "recall": 0.9159, + "filter_ratio": 0.8 }, { "dataset": "Cohere (Large)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 187.4268, - "latency": 453.7, - "recall": 0.4692, - "filter_ratio": 0.999 + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 8633.8949, + "latency": 4.1, + "recall": 0.8928, + "filter_ratio": 0.9 }, { "dataset": "Cohere (Large)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 198.397, - "latency": 506.9, - "recall": 0.5409, - "filter_ratio": 0.998 + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 9220.3627, + "latency": 3.8, + "recall": 0.9081, + "filter_ratio": 0.95 }, { "dataset": "Cohere (Large)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 174.3549, - "latency": 496.9, - "recall": 0.6279, - "filter_ratio": 0.995 + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 9368.1325, + "latency": 3.8, + "recall": 0.9292, + "filter_ratio": 0.98 }, { "dataset": "Cohere (Large)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 172.95, - "latency": 515.6, - "recall": 0.7004, + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 9374.8941, + "latency": 4.2, + "recall": 0.9425, "filter_ratio": 0.99 }, { "dataset": "Cohere (Large)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 190.9747, - "latency": 517.4, - "recall": 0.7398, - "filter_ratio": 0.98 + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 9289.0118, + "latency": 4.2, + "recall": 0.9574, + "filter_ratio": 0.995 }, { "dataset": "Cohere (Large)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 186.0237, - "latency": 474.0, - "recall": 0.7847, - "filter_ratio": 0.95 + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 9244.1135, + "latency": 4.2, + "recall": 0.9724, + "filter_ratio": 0.998 }, { "dataset": "Cohere (Large)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 192.1458, - "latency": 480.5, - "recall": 0.8103, - "filter_ratio": 0.9 + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 8695.2765, + "latency": 4.3, + "recall": 0.9603, + "filter_ratio": 0.999 }, { - "dataset": "Cohere (Large)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 179.4203, - "latency": 497.5, - "recall": 0.8273, + "dataset": "Cohere (Medium)", + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 9048.6431, + "latency": 3.9, + "recall": 0.9216, + "filter_ratio": 0.5 + }, + { + "dataset": "Cohere (Medium)", + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 9428.4531, + "latency": 2.6, + "recall": 0.9331, "filter_ratio": 0.8 }, { - "dataset": "Cohere (Large)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "qps": 199.5444, - "latency": 463.9, - "recall": 0.8478, - "filter_ratio": 0.5 + "dataset": "Cohere (Medium)", + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 9507.9991, + "latency": 2.8, + "recall": 0.9453, + "filter_ratio": 0.9 + }, + { + "dataset": "Cohere (Medium)", + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 9861.9686, + "latency": 2.6, + "recall": 0.955, + "filter_ratio": 0.95 + }, + { + "dataset": "Cohere (Medium)", + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 10041.0338, + "latency": 2.7, + "recall": 0.9693, + "filter_ratio": 0.98 + }, + { + "dataset": "Cohere (Medium)", + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 10020.5299, + "latency": 2.6, + "recall": 0.9788, + "filter_ratio": 0.99 + }, + { + "dataset": "Cohere (Medium)", + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 9805.0401, + "latency": 2.6, + "recall": 0.9257, + "filter_ratio": 0.995 + }, + { + "dataset": "Cohere (Medium)", + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 10557.4373, + "latency": 2.7, + "recall": 0.9393, + "filter_ratio": 0.998 + }, + { + "dataset": "Cohere (Medium)", + "db": "ZillizCloud", + "label": "8cu-perf-partition_key", + "db_name": "ZillizCloud-8cu-perf-partition_key", + "qps": 10089.4308, + "latency": 2.6, + "recall": 0.9934, + "filter_ratio": 0.999 } ] \ No newline at end of file diff --git a/vectordb_bench/results/leaderboard_v2_streaming.json b/vectordb_bench/results/leaderboard_v2_streaming.json index 224c78d12..73075e8a8 100644 --- a/vectordb_bench/results/leaderboard_v2_streaming.json +++ b/vectordb_bench/results/leaderboard_v2_streaming.json @@ -1,128 +1,146 @@ [ - { - "dataset": "Cohere (Large)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "insert_rate": 500, - "streaming_qps": 61.6708, - "streaming_latency": 0.0794 - }, - { - "dataset": "Cohere (Large)", - "db": "ElasticCloud", - "label": "8c60g", - "db_name": "ElasticCloud-8c60g", - "insert_rate": 1000, - "streaming_qps": 61.8172, - "streaming_latency": 0.2223 - }, - { - "dataset": "Cohere (Large)", - "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "insert_rate": 500, - "streaming_qps": 305.9971, - "streaming_latency": 0.005 - }, - { - "dataset": "Cohere (Large)", - "db": "Milvus", - "label": "16c64g-sq8", - "db_name": "Milvus-16c64g-sq8", - "insert_rate": 1000, - "streaming_qps": 155.9613, - "streaming_latency": 0.0203 - }, - { - "dataset": "Cohere (Large)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "insert_rate": 500, - "streaming_qps": 367.4299, - "streaming_latency": 1.8286 - }, - { - "dataset": "Cohere (Large)", - "db": "Pinecone", - "label": "p2.x8-1node", - "db_name": "Pinecone-p2.x8-1node", - "insert_rate": 1000, - "streaming_qps": 369.6771, - "streaming_latency": 5.992 - }, - { - "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g", - "db_name": "QdrantCloud-16c64g", - "insert_rate": 500, - "streaming_qps": 393.753, - "streaming_latency": 0.0162 - }, - { - "dataset": "Cohere (Large)", - "db": "QdrantCloud", - "label": "16c64g", - "db_name": "QdrantCloud-16c64g", - "insert_rate": 1000, - "streaming_qps": 347.5774, - "streaming_latency": 0.0118 - }, - { - "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "insert_rate": 1000, - "streaming_qps": 149.7168, - "streaming_latency": 0.098 - }, - { - "dataset": "Cohere (Large)", - "db": "OpenSearch", - "label": "16c128g", - "db_name": "OpenSearch-16c128g", - "insert_rate": 500, - "streaming_qps": 161.6694, - "streaming_latency": 0.052 - }, - { - "dataset": "Cohere (Large)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "insert_rate": 500, - "streaming_qps": 2118.7516, - "streaming_latency": 0.0068 - }, - { - "dataset": "Cohere (Large)", - "db": "ZillizCloud", - "label": "8cu-perf", - "db_name": "ZillizCloud-8cu-perf", - "insert_rate": 1000, - "streaming_qps": 1860.2575, - "streaming_latency": 0.0101 - }, - { - "dataset": "Cohere (Large)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "insert_rate": 500, - "streaming_qps": 180.9549, - "streaming_latency": 0.4204 - }, - { - "dataset": "Cohere (Large)", - "db": "S3Vectors", - "label": "", - "db_name": "S3Vectors", - "insert_rate": 1000, - "streaming_qps": 167.2689, - "streaming_latency": 0.5048 - } + { + "dataset": "Cohere (Large)", + "db": "ElasticCloud", + "label": "8c60g-force_merge", + "db_name": "ElasticCloud-8c60g-force_merge", + "insert_rate": 500, + "streaming_qps": 61.6708, + "streaming_latency": 0.0794 + }, + { + "dataset": "Cohere (Large)", + "db": "ElasticCloud", + "label": "8c60g-force_merge", + "db_name": "ElasticCloud-8c60g-force_merge", + "insert_rate": 1000, + "streaming_qps": 61.8172, + "streaming_latency": 0.2223 + }, + { + "dataset": "Cohere (Large)", + "db": "Milvus", + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "insert_rate": 500, + "streaming_qps": 305.9971, + "streaming_latency": 0.005 + }, + { + "dataset": "Cohere (Large)", + "db": "Milvus", + "label": "16c64g-sq8-force_merge", + "db_name": "Milvus-16c64g-sq8-force_merge", + "insert_rate": 1000, + "streaming_qps": 155.9613, + "streaming_latency": 0.0203 + }, + { + "dataset": "Cohere (Large)", + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "insert_rate": 500, + "streaming_qps": 367.4299, + "streaming_latency": 1.8286 + }, + { + "dataset": "Cohere (Large)", + "db": "Pinecone", + "label": "p2.x8-1node", + "db_name": "Pinecone-p2.x8-1node", + "insert_rate": 1000, + "streaming_qps": 369.6771, + "streaming_latency": 5.992 + }, + { + "dataset": "Cohere (Large)", + "db": "QdrantCloud", + "label": "16c64g", + "db_name": "QdrantCloud-16c64g", + "insert_rate": 500, + "streaming_qps": 393.753, + "streaming_latency": 0.0162 + }, + { + "dataset": "Cohere (Large)", + "db": "QdrantCloud", + "label": "16c64g", + "db_name": "QdrantCloud-16c64g", + "insert_rate": 1000, + "streaming_qps": 347.5774, + "streaming_latency": 0.0118 + }, + { + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "insert_rate": 1000, + "streaming_qps": 149.7168, + "streaming_latency": 0.098 + }, + { + "dataset": "Cohere (Large)", + "db": "OpenSearch", + "label": "16c128g", + "db_name": "OpenSearch-16c128g", + "insert_rate": 500, + "streaming_qps": 161.6694, + "streaming_latency": 0.052 + }, + { + "dataset": "Cohere (Large)", + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "insert_rate": 500, + "streaming_qps": 2118.7516, + "streaming_latency": 0.0068 + }, + { + "dataset": "Cohere (Large)", + "db": "ZillizCloud", + "label": "8cu-perf", + "db_name": "ZillizCloud-8cu-perf", + "insert_rate": 1000, + "streaming_qps": 1860.2575, + "streaming_latency": 0.0101 + }, + { + "dataset": "Cohere (Large)", + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "insert_rate": 500, + "streaming_qps": 180.9549, + "streaming_latency": 0.4204 + }, + { + "dataset": "Cohere (Large)", + "db": "S3Vectors", + "label": "", + "db_name": "S3Vectors", + "insert_rate": 1000, + "streaming_qps": 167.2689, + "streaming_latency": 0.5048 + }, + { + "dataset": "Cohere (Large)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "insert_rate": 500, + "streaming_qps": 536.0198, + "streaming_latency": 0.3132 + }, + { + "dataset": "Cohere (Large)", + "db": "TurboPuffer", + "label": "2026-03-31", + "db_name": "TurboPuffer", + "insert_rate": 1000, + "streaming_qps": 442.5824, + "streaming_latency": 0.0724 + } ] \ No newline at end of file