diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ce226199..25bc8d22 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,8 +8,8 @@ on: - main schedule: - - cron: "0 2 * * *" # 2 AM UTC nightly - + - cron: "0 2 * * *" # 2 AM UTC nightly + workflow_dispatch: env: @@ -17,8 +17,8 @@ env: POETRY_VERSION: "1.8.3" jobs: - prime-cache: - name: Prime HuggingFace Model Cache + service-tests: + name: Service Tests runs-on: ubuntu-latest env: HF_HOME: ${{ github.workspace }}/hf_cache @@ -38,10 +38,10 @@ jobs: mkdir -p ~/.huggingface echo '{"token":"${{ secrets.HF_TOKEN }}"}' > ~/.huggingface/token - - name: Set up Python 3.9 + - name: Set up Python 3.11 uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: 3.11 cache: pip - name: Install Poetry @@ -58,7 +58,7 @@ jobs: with: credentials_json: ${{ secrets.GOOGLE_CREDENTIALS }} - - name: Run full test suite to prime cache + - name: Run full test suite and prime the HF cache env: HF_TOKEN: ${{ secrets.HF_TOKEN }} HF_HOME: ${{ github.workspace }}/hf_cache @@ -80,15 +80,16 @@ jobs: test: name: Python ${{ matrix.python-version }} - ${{ matrix.connection }} [redis ${{ matrix.redis-version }}] runs-on: ubuntu-latest - needs: prime-cache + needs: service-tests env: HF_HOME: ${{ github.workspace }}/hf_cache strategy: fail-fast: false matrix: - python-version: ['3.10', '3.11', 3.12, 3.13] - connection: ['hiredis', 'plain'] - redis-version: ['6.2.6-v9', 'latest', '8.0-M03'] + # 3.11 tests are run in the service-tests job + python-version: ["3.9", "3.10", 3.12, 3.13] + connection: ["hiredis", "plain"] + redis-version: ["6.2.6-v9", "latest", "8.0-M03"] steps: - name: Check out repository @@ -137,20 +138,10 @@ jobs: if: matrix.connection == 'plain' && matrix.redis-version == 'latest' env: HF_HOME: ${{ github.workspace }}/hf_cache - OPENAI_API_KEY: ${{ secrets.OPENAI_KEY }} GCP_LOCATION: ${{ secrets.GCP_LOCATION }} GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} - COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }} - MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} - VOYAGE_API_KEY: ${{ secrets.VOYAGE_API_KEY }} - AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }} - AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }} - AZURE_OPENAI_DEPLOYMENT_NAME: ${{ secrets.AZURE_OPENAI_DEPLOYMENT_NAME }} - OPENAI_API_VERSION: ${{ secrets.OPENAI_API_VERSION }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} run: | - make test-all + make test - name: Run tests (alternate) if: matrix.connection != 'plain' || matrix.redis-version != 'latest' @@ -173,9 +164,14 @@ jobs: OPENAI_API_VERSION: ${{ secrets.OPENAI_API_VERSION }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | docker run -d --name redis -p 6379:6379 redis/redis-stack-server:latest - make test-notebooks + if [[ "${{ matrix.python-version }}" > "3.9" ]]; then + make test-notebooks + else + poetry run test-notebooks --ignore ./docs/user_guide/09_threshold_optimization.ipynb --ignore ./docs/user_guide/release_guide/0_5_0_release.ipynb + fi docs: runs-on: ubuntu-latest diff --git a/README.md b/README.md index 1fb2bee6..20712e4f 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,11 @@ embeddings = co.embed_many( ### Rerankers [Integrate with popular reranking providers](https://docs.redisvl.com/en/stable/user_guide/06_rerankers.html) to improve the relevancy of the initial search results from Redis +### Threshold Optimization +[Optimize distance thresholds for cache and router](https://docs.redisvl.com/en/stable/user_guide/09_threshold_optimization.html) with the utility `ThresholdOptimizer` classes. + +**Note:** only available for `python > 3.9`. + ## 💫 Extensions diff --git a/docs/api/index.md b/docs/api/index.md index 598d9dcd..e3dc486e 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -22,5 +22,6 @@ reranker cache session_manager router +threshold_optimizer ``` diff --git a/docs/api/query.rst b/docs/api/query.rst index 8086f5ca..fa92230e 100644 --- a/docs/api/query.rst +++ b/docs/api/query.rst @@ -34,6 +34,34 @@ VectorRangeQuery :show-inheritance: :exclude-members: add_filter,get_args,highlight,return_field,summarize +HybridQuery +================ + + +.. currentmodule:: redisvl.query + + +.. autoclass:: HybridQuery + :members: + :inherited-members: + :show-inheritance: + :exclude-members: add_filter,get_args,highlight,return_field,summarize + + +TextQuery +================ + + +.. currentmodule:: redisvl.query + + +.. autoclass:: TextQuery + :members: + :inherited-members: + :show-inheritance: + :exclude-members: add_filter,get_args,highlight,return_field,summarize + + FilterQuery =========== diff --git a/docs/api/session_manager.rst b/docs/api/session_manager.rst index 9b885a35..86289204 100644 --- a/docs/api/session_manager.rst +++ b/docs/api/session_manager.rst @@ -2,7 +2,6 @@ LLM Session Manager ******************* - SemanticSessionManager ====================== diff --git a/docs/api/threshold_optimizer.rst b/docs/api/threshold_optimizer.rst new file mode 100644 index 00000000..dc226cef --- /dev/null +++ b/docs/api/threshold_optimizer.rst @@ -0,0 +1,26 @@ +******************** +Threshold Optimizers +******************** + +CacheThresholdOptimizer +======================= + +.. _cachethresholdoptimizer_api: + +.. currentmodule:: redisvl.utils.optimize.cache + +.. autoclass:: CacheThresholdOptimizer + :show-inheritance: + :members: + + +RouterThresholdOptimizer +======================== + +.. _routerthresholdoptimizer_api: + +.. currentmodule:: redisvl.utils.optimize.router + +.. autoclass:: RouterThresholdOptimizer + :show-inheritance: + :members: diff --git a/docs/user_guide/01_getting_started.ipynb b/docs/user_guide/01_getting_started.ipynb index 6130f589..bf097415 100644 --- a/docs/user_guide/01_getting_started.ipynb +++ b/docs/user_guide/01_getting_started.ipynb @@ -81,7 +81,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -126,7 +126,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -173,29 +173,7 @@ "source": [ "## Create a `SearchIndex`\n", "\n", - "With the schema and sample dataset ready, instantiate a `SearchIndex`:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "from redisvl.index import SearchIndex\n", - "\n", - "index = SearchIndex.from_dict(schema)\n", - "# or use .from_yaml('schema_file.yaml')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we also need to facilitate a Redis connection. There are a few ways to do this:\n", - "\n", - "- Create & manage your own client connection (recommended)\n", - "- Provide a Redis URL and let RedisVL connect on your behalf (by default, it will connect to \"redis://localhost:6379\")" + "With the schema and sample dataset ready, create a `SearchIndex`." ] }, { @@ -209,31 +187,15 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 11, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ + "from redisvl.index import SearchIndex\n", "from redis import Redis\n", "\n", "client = Redis.from_url(\"redis://localhost:6379\")\n", - "index = SearchIndex.from_dict(schema, redis_client=client)\n", - "\n", - "# alternatively, provide an async Redis client object to enable async index operations\n", - "# from redis.asyncio import Redis\n", - "# from redisvl.index import AsyncSearchIndex\n", - "# client = Redis.from_url(\"redis://localhost:6379\")\n", - "# index = AsyncSearchIndex.from_dict(schema, redis_client=client)\n" + "index = SearchIndex.from_dict(schema, redis_client=client, validate_on_load=True)" ] }, { @@ -262,24 +224,24 @@ } ], "source": [ - "index = SearchIndex.from_dict(schema, redis_url=\"redis://localhost:6379\")\n", + "index = SearchIndex.from_dict(schema, redis_url=\"redis://localhost:6379\", validate_on_load=True)\n", "\n", "# If you don't specify a client or Redis URL, the index will attempt to\n", - "# connect to Redis at the default address (\"redis://localhost:6379\")." + "# connect to Redis at the default address \"redis://localhost:6379\"." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Create the underlying index\n", + "### Create the index\n", "\n", "Now that we are connected to Redis, we need to run the create command." ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -303,15 +265,15 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "\u001b[32m11:50:15\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m Indices:\n", - "\u001b[32m11:50:15\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 1. user_simple\n" + "\u001b[32m10:59:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m Indices:\n", + "\u001b[32m10:59:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 1. user_simple\n" ] } ], @@ -321,7 +283,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -359,19 +321,22 @@ "source": [ "## Load Data to `SearchIndex`\n", "\n", - "Load the sample dataset to Redis:" + "Load the sample dataset to Redis.\n", + "\n", + "### Validate data entries on load\n", + "RedisVL uses pydantic validation under the hood to ensure loaded data is valid and confirms to your schema. This setting is optional and can be configured in the `SearchIndex` class." ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "['user_simple_docs:01JM2NWFWNH0BNA640MT5DS8BD', 'user_simple_docs:01JM2NWFWNF4S2V4E4HYG25CVA', 'user_simple_docs:01JM2NWFWNBFXJJ4PV9F4KMJSE']\n" + "['user_simple_docs:01JQ9FEZ4GAAYT9W7BWAF7CV18', 'user_simple_docs:01JQ9FEZ4JCE5FD1D5QY6BAJ0J', 'user_simple_docs:01JQ9FEZ4KF9AZYBKMYNMYBZ5A']\n" ] } ], @@ -388,6 +353,98 @@ ">By default, `load` will create a unique Redis key as a combination of the index key `prefix` and a random ULID. You can also customize the key by providing direct keys or pointing to a specified `id_field` on load." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load invalid data\n", + "This will raise a `SchemaValidationError` if `validate_on_load` is set to true in the `SearchIndex` class." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "11:00:03 redisvl.index.index ERROR Schema validation error while loading data\n", + "Traceback (most recent call last):\n", + " File \"/Users/tyler.hutcherson/Documents/AppliedAI/redis-vl-python/redisvl/index/storage.py\", line 204, in _preprocess_and_validate_objects\n", + " processed_obj = self._validate(processed_obj)\n", + " File \"/Users/tyler.hutcherson/Documents/AppliedAI/redis-vl-python/redisvl/index/storage.py\", line 160, in _validate\n", + " return validate_object(self.index_schema, obj)\n", + " File \"/Users/tyler.hutcherson/Documents/AppliedAI/redis-vl-python/redisvl/schema/validation.py\", line 274, in validate_object\n", + " validated = model_class.model_validate(flat_obj)\n", + " File \"/Users/tyler.hutcherson/Library/Caches/pypoetry/virtualenvs/redisvl-VnTEShF2-py3.13/lib/python3.13/site-packages/pydantic/main.py\", line 627, in model_validate\n", + " return cls.__pydantic_validator__.validate_python(\n", + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^\n", + " obj, strict=strict, from_attributes=from_attributes, context=context\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " )\n", + " ^\n", + "pydantic_core._pydantic_core.ValidationError: 1 validation error for user_simple__PydanticModel\n", + "user_embedding\n", + " Input should be a valid bytes [type=bytes_type, input_value=True, input_type=bool]\n", + " For further information visit https://errors.pydantic.dev/2.10/v/bytes_type\n", + "\n", + "The above exception was the direct cause of the following exception:\n", + "\n", + "Traceback (most recent call last):\n", + " File \"/Users/tyler.hutcherson/Documents/AppliedAI/redis-vl-python/redisvl/index/index.py\", line 586, in load\n", + " return self._storage.write(\n", + " ~~~~~~~~~~~~~~~~~~~^\n", + " self._redis_client, # type: ignore\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " ...<6 lines>...\n", + " validate=self._validate_on_load,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " )\n", + " ^\n", + " File \"/Users/tyler.hutcherson/Documents/AppliedAI/redis-vl-python/redisvl/index/storage.py\", line 265, in write\n", + " prepared_objects = self._preprocess_and_validate_objects(\n", + " list(objects), # Convert Iterable to List\n", + " ...<3 lines>...\n", + " validate=validate,\n", + " )\n", + " File \"/Users/tyler.hutcherson/Documents/AppliedAI/redis-vl-python/redisvl/index/storage.py\", line 211, in _preprocess_and_validate_objects\n", + " raise SchemaValidationError(str(e), index=i) from e\n", + "redisvl.exceptions.SchemaValidationError: Validation failed for object at index 0: 1 validation error for user_simple__PydanticModel\n", + "user_embedding\n", + " Input should be a valid bytes [type=bytes_type, input_value=True, input_type=bool]\n", + " For further information visit https://errors.pydantic.dev/2.10/v/bytes_type\n" + ] + }, + { + "ename": "SchemaValidationError", + "evalue": "Validation failed for object at index 0: 1 validation error for user_simple__PydanticModel\nuser_embedding\n Input should be a valid bytes [type=bytes_type, input_value=True, input_type=bool]\n For further information visit https://errors.pydantic.dev/2.10/v/bytes_type", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValidationError\u001b[0m Traceback (most recent call last)", + "File \u001b[0;32m~/Documents/AppliedAI/redis-vl-python/redisvl/index/storage.py:204\u001b[0m, in \u001b[0;36mBaseStorage._preprocess_and_validate_objects\u001b[0;34m(self, objects, id_field, keys, preprocess, validate)\u001b[0m\n\u001b[1;32m 203\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m validate:\n\u001b[0;32m--> 204\u001b[0m processed_obj \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_validate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mprocessed_obj\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 206\u001b[0m \u001b[38;5;66;03m# Store valid object with its key for writing\u001b[39;00m\n", + "File \u001b[0;32m~/Documents/AppliedAI/redis-vl-python/redisvl/index/storage.py:160\u001b[0m, in \u001b[0;36mBaseStorage._validate\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 159\u001b[0m \u001b[38;5;66;03m# Pass directly to validation function and let any errors propagate\u001b[39;00m\n\u001b[0;32m--> 160\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mvalidate_object\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mindex_schema\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/Documents/AppliedAI/redis-vl-python/redisvl/schema/validation.py:274\u001b[0m, in \u001b[0;36mvalidate_object\u001b[0;34m(schema, obj)\u001b[0m\n\u001b[1;32m 273\u001b[0m \u001b[38;5;66;03m# Validate against model\u001b[39;00m\n\u001b[0;32m--> 274\u001b[0m validated \u001b[38;5;241m=\u001b[39m \u001b[43mmodel_class\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmodel_validate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mflat_obj\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 275\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m validated\u001b[38;5;241m.\u001b[39mmodel_dump(exclude_none\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n", + "File \u001b[0;32m~/Library/Caches/pypoetry/virtualenvs/redisvl-VnTEShF2-py3.13/lib/python3.13/site-packages/pydantic/main.py:627\u001b[0m, in \u001b[0;36mBaseModel.model_validate\u001b[0;34m(cls, obj, strict, from_attributes, context)\u001b[0m\n\u001b[1;32m 626\u001b[0m __tracebackhide__ \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m--> 627\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__pydantic_validator__\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_python\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 628\u001b[0m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstrict\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstrict\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfrom_attributes\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfrom_attributes\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcontext\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcontext\u001b[49m\n\u001b[1;32m 629\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mValidationError\u001b[0m: 1 validation error for user_simple__PydanticModel\nuser_embedding\n Input should be a valid bytes [type=bytes_type, input_value=True, input_type=bool]\n For further information visit https://errors.pydantic.dev/2.10/v/bytes_type", + "\nThe above exception was the direct cause of the following exception:\n", + "\u001b[0;31mSchemaValidationError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[16], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m keys \u001b[38;5;241m=\u001b[39m \u001b[43mindex\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mload\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43muser_embedding\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m}\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/Documents/AppliedAI/redis-vl-python/redisvl/index/index.py:586\u001b[0m, in \u001b[0;36mSearchIndex.load\u001b[0;34m(self, data, id_field, keys, ttl, preprocess, batch_size)\u001b[0m\n\u001b[1;32m 556\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Load objects to the Redis database. Returns the list of keys loaded\u001b[39;00m\n\u001b[1;32m 557\u001b[0m \u001b[38;5;124;03mto Redis.\u001b[39;00m\n\u001b[1;32m 558\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 583\u001b[0m \u001b[38;5;124;03m RedisVLError: If there's an error loading data to Redis.\u001b[39;00m\n\u001b[1;32m 584\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 585\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 586\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_storage\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mwrite\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 587\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_redis_client\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore\u001b[39;49;00m\n\u001b[1;32m 588\u001b[0m \u001b[43m \u001b[49m\u001b[43mobjects\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mid_field\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mid_field\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkeys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mttl\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mttl\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mpreprocess\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpreprocess\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mbatch_size\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbatch_size\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[43mvalidate\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_validate_on_load\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 596\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m SchemaValidationError:\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Pass through validation errors directly\u001b[39;00m\n\u001b[1;32m 598\u001b[0m logger\u001b[38;5;241m.\u001b[39mexception(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mSchema validation error while loading data\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", + "File \u001b[0;32m~/Documents/AppliedAI/redis-vl-python/redisvl/index/storage.py:265\u001b[0m, in \u001b[0;36mBaseStorage.write\u001b[0;34m(self, redis_client, objects, id_field, keys, ttl, preprocess, batch_size, validate)\u001b[0m\n\u001b[1;32m 262\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m []\n\u001b[1;32m 264\u001b[0m \u001b[38;5;66;03m# Pass 1: Preprocess and validate all objects\u001b[39;00m\n\u001b[0;32m--> 265\u001b[0m prepared_objects \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_preprocess_and_validate_objects\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 266\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mobjects\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# Convert Iterable to List\u001b[39;49;00m\n\u001b[1;32m 267\u001b[0m \u001b[43m \u001b[49m\u001b[43mid_field\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mid_field\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 268\u001b[0m \u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkeys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 269\u001b[0m \u001b[43m \u001b[49m\u001b[43mpreprocess\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpreprocess\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 270\u001b[0m \u001b[43m \u001b[49m\u001b[43mvalidate\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mvalidate\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 271\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 273\u001b[0m \u001b[38;5;66;03m# Pass 2: Write all valid objects in batches\u001b[39;00m\n\u001b[1;32m 274\u001b[0m added_keys \u001b[38;5;241m=\u001b[39m []\n", + "File \u001b[0;32m~/Documents/AppliedAI/redis-vl-python/redisvl/index/storage.py:211\u001b[0m, in \u001b[0;36mBaseStorage._preprocess_and_validate_objects\u001b[0;34m(self, objects, id_field, keys, preprocess, validate)\u001b[0m\n\u001b[1;32m 207\u001b[0m prepared_objects\u001b[38;5;241m.\u001b[39mappend((key, processed_obj))\n\u001b[1;32m 209\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m ValidationError \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 210\u001b[0m \u001b[38;5;66;03m# Convert Pydantic ValidationError to SchemaValidationError with index context\u001b[39;00m\n\u001b[0;32m--> 211\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m SchemaValidationError(\u001b[38;5;28mstr\u001b[39m(e), index\u001b[38;5;241m=\u001b[39mi) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01me\u001b[39;00m\n\u001b[1;32m 212\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 213\u001b[0m \u001b[38;5;66;03m# Capture other exceptions with context\u001b[39;00m\n\u001b[1;32m 214\u001b[0m object_id \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mat index \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mi\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n", + "\u001b[0;31mSchemaValidationError\u001b[0m: Validation failed for object at index 0: 1 validation error for user_simple__PydanticModel\nuser_embedding\n Input should be a valid bytes [type=bytes_type, input_value=True, input_type=bool]\n For further information visit https://errors.pydantic.dev/2.10/v/bytes_type" + ] + } + ], + "source": [ + "# NBVAL_SKIP\n", + "\n", + "keys = index.load([{\"user_embedding\": True}])" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -398,14 +455,14 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "['user_simple_docs:01JM2NWJGYMJ0QTR5YB4MB0BX9']\n" + "['user_simple_docs:01JQ9FHCB1B64GXF6WPK127VZ6']\n" ] } ], @@ -435,7 +492,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -460,20 +517,13 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 19, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "*=>[KNN 3 @user_embedding $vector AS vector_distance] RETURN 6 user age job credit_score vector_distance vector_distance SORTBY vector_distance ASC DIALECT 2 LIMIT 0 3\n" - ] - }, { "data": { "text/html": [ - "table>vector_distanceuseragejobcredit_score0john1engineerhigh0mary2doctorlow0.0566299557686tyler9engineerhigh" + "
vector_distanceuseragejobcredit_score
0john1engineerhigh
0mary2doctorlow
0.0566299557686tyler9engineerhigh
" ], "text/plain": [ "" @@ -500,7 +550,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -519,7 +569,7 @@ " 'datatype': 'float32'}}]}" ] }, - "execution_count": 13, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -530,32 +580,20 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 21, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "from redisvl.index import AsyncSearchIndex\n", "from redis.asyncio import Redis\n", "\n", "client = Redis.from_url(\"redis://localhost:6379\")\n", - "\n", "index = AsyncSearchIndex.from_dict(schema, redis_client=client)" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -596,7 +634,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -621,14 +659,14 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "11:28:32 redisvl.index.index INFO Index already exists, overwriting.\n" + "11:01:30 redisvl.index.index INFO Index already exists, overwriting.\n" ] } ], @@ -639,13 +677,13 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
vector_distanceuseragejobcredit_score
0mary2doctorlow
0john1engineerhigh
0.0566299557686tyler9engineerhigh
" + "
vector_distanceuseragejobcredit_score
0john1engineerhigh
0mary2doctorlow
0.0566299557686tyler9engineerhigh
" ], "text/plain": [ "" @@ -671,7 +709,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -684,9 +722,9 @@ "│ Stat Key │ Value │\n", "├─────────────────────────────┼─────────────┤\n", "│ num_docs │ 4 │\n", - "│ num_terms │ 4 │\n", + "│ num_terms │ 0 │\n", "│ max_doc_id │ 4 │\n", - "│ num_records │ 22 │\n", + "│ num_records │ 20 │\n", "│ percent_indexed │ 1 │\n", "│ hash_indexing_failures │ 0 │\n", "│ number_of_uses │ 2 │\n", @@ -699,9 +737,9 @@ "│ offsets_per_term_avg │ 0 │\n", "│ records_per_doc_avg │ 5 │\n", "│ sortable_values_size_mb │ 0 │\n", - "│ total_indexing_time │ 0.239 │\n", + "│ total_indexing_time │ 6.529 │\n", "│ total_inverted_index_blocks │ 11 │\n", - "│ vector_index_sz_mb │ 0.235603 │\n", + "│ vector_index_sz_mb │ 0.235947 │\n", "╰─────────────────────────────┴─────────────╯\n" ] } @@ -730,7 +768,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -739,7 +777,7 @@ "4" ] }, - "execution_count": 21, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -751,7 +789,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -760,7 +798,7 @@ "True" ] }, - "execution_count": 22, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -772,7 +810,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ diff --git a/docs/user_guide/02_hybrid_queries.ipynb b/docs/user_guide/02_hybrid_queries.ipynb index 9568669d..3d399dcc 100644 --- a/docs/user_guide/02_hybrid_queries.ipynb +++ b/docs/user_guide/02_hybrid_queries.ipynb @@ -22,7 +22,7 @@ { "data": { "text/html": [ - "
useragejobcredit_scoreoffice_locationuser_embedding
john18engineerhigh-122.4194,37.7749b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'
derrick14doctorlow-122.4194,37.7749b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'
nancy94doctorhigh-122.4194,37.7749b'333?\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'
tyler100engineerhigh-122.0839,37.3861b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc>\\x00\\x00\\x00?'
tim12dermatologisthigh-122.0839,37.3861b'\\xcd\\xcc\\xcc>\\xcd\\xcc\\xcc>\\x00\\x00\\x00?'
taimur15CEOlow-122.0839,37.3861b'\\x9a\\x99\\x19?\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'
joe35dentistmedium-122.0839,37.3861b'fff?fff?\\xcd\\xcc\\xcc='
" + "
useragejobcredit_scoreoffice_locationuser_embeddinglast_updated
john18engineerhigh-122.4194,37.7749b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'1741627789
derrick14doctorlow-122.4194,37.7749b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'1741627789
nancy94doctorhigh-122.4194,37.7749b'333?\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'1710696589
tyler100engineerhigh-122.0839,37.3861b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc>\\x00\\x00\\x00?'1742232589
tim12dermatologisthigh-122.0839,37.3861b'\\xcd\\xcc\\xcc>\\xcd\\xcc\\xcc>\\x00\\x00\\x00?'1739644189
taimur15CEOlow-122.0839,37.3861b'\\x9a\\x99\\x19?\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'1742232589
joe35dentistmedium-122.0839,37.3861b'fff?fff?\\xcd\\xcc\\xcc='1742232589
" ], "text/plain": [ "" @@ -58,6 +58,7 @@ " {\"name\": \"credit_score\", \"type\": \"tag\"},\n", " {\"name\": \"job\", \"type\": \"text\"},\n", " {\"name\": \"age\", \"type\": \"numeric\"},\n", + " {\"name\": \"last_updated\", \"type\": \"numeric\"},\n", " {\"name\": \"office_location\", \"type\": \"geo\"},\n", " {\n", " \"name\": \"user_embedding\",\n", @@ -83,7 +84,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "13:02:18 redisvl.index.index INFO Index already exists, overwriting.\n" + "11:40:25 redisvl.index.index INFO Index already exists, overwriting.\n" ] } ], @@ -99,28 +100,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[32m13:02:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m Indices:\n", - "\u001b[32m13:02:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 1. float64_cache\n", - "\u001b[32m13:02:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 2. float64_session\n", - "\u001b[32m13:02:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 3. float16_cache\n", - "\u001b[32m13:02:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 4. float16_session\n", - "\u001b[32m13:02:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 5. float32_session\n", - "\u001b[32m13:02:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 6. float32_cache\n", - "\u001b[32m13:02:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 7. bfloat_cache\n", - "\u001b[32m13:02:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 8. user_queries\n", - "\u001b[32m13:02:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 9. student tutor\n", - "\u001b[32m13:02:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 10. tutor\n", - "\u001b[32m13:02:25\u001b[0m \u001b[34m[RedisVL]\u001b[0m \u001b[1;30mINFO\u001b[0m 11. bfloat_session\n" - ] - } - ], + "outputs": [], "source": [ "# use the CLI to see the created index\n", "!rvl index listall" @@ -128,7 +110,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -136,6 +118,26 @@ "keys = index.load(data)" ] }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "index.info()['num_docs']" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -157,13 +159,13 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
vector_distanceusercredit_scoreagejoboffice_location
0johnhigh18engineer-122.4194,37.7749
0johnhigh18engineer-122.4194,37.7749
0.109129190445tylerhigh100engineer-122.0839,37.3861
0.109129190445tylerhigh100engineer-122.0839,37.3861
0.158808946609timhigh12dermatologist-122.0839,37.3861
0.158808946609timhigh12dermatologist-122.0839,37.3861
0.266666650772nancyhigh94doctor-122.4194,37.7749
0.266666650772nancyhigh94doctor-122.4194,37.7749
" + "
vector_distanceusercredit_scoreagejoboffice_locationlast_updated
0johnhigh18engineer-122.4194,37.77491741627789
0.109129190445tylerhigh100engineer-122.0839,37.38611742232589
0.158808946609timhigh12dermatologist-122.0839,37.38611739644189
0.266666650772nancyhigh94doctor-122.4194,37.77491710696589
" ], "text/plain": [ "" @@ -182,7 +184,7 @@ "v = VectorQuery(\n", " vector=[0.1, 0.1, 0.5],\n", " vector_field_name=\"user_embedding\",\n", - " return_fields=[\"user\", \"credit_score\", \"age\", \"job\", \"office_location\"],\n", + " return_fields=[\"user\", \"credit_score\", \"age\", \"job\", \"office_location\", \"last_updated\"],\n", " filter_expression=t\n", ")\n", "\n", @@ -192,13 +194,13 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
vector_distanceusercredit_scoreagejoboffice_location
0derricklow14doctor-122.4194,37.7749
0derricklow14doctor-122.4194,37.7749
0.217882037163taimurlow15CEO-122.0839,37.3861
0.217882037163taimurlow15CEO-122.0839,37.3861
0.653301358223joemedium35dentist-122.0839,37.3861
0.653301358223joemedium35dentist-122.0839,37.3861
" + "
vector_distanceusercredit_scoreagejoboffice_locationlast_updated
0derricklow14doctor-122.4194,37.77491741627789
0.217882037163taimurlow15CEO-122.0839,37.38611742232589
0.653301358223joemedium35dentist-122.0839,37.38611742232589
" ], "text/plain": [ "" @@ -316,13 +318,13 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
vector_distanceusercredit_scoreagejoboffice_location
0johnhigh18engineer-122.4194,37.7749
0johnhigh18engineer-122.4194,37.7749
0.109129190445tylerhigh100engineer-122.0839,37.3861
0.109129190445tylerhigh100engineer-122.0839,37.3861
0.266666650772nancyhigh94doctor-122.4194,37.7749
0.266666650772nancyhigh94doctor-122.4194,37.7749
0.653301358223joemedium35dentist-122.0839,37.3861
0.653301358223joemedium35dentist-122.0839,37.3861
" + "
vector_distanceusercredit_scoreagejoboffice_locationlast_updated
0johnhigh18engineer-122.4194,37.77491741627789
0.217882037163taimurlow15CEO-122.0839,37.38611742232589
0.653301358223joemedium35dentist-122.0839,37.38611742232589
" ], "text/plain": [ "" @@ -335,7 +337,7 @@ "source": [ "from redisvl.query.filter import Num\n", "\n", - "numeric_filter = Num(\"age\") > 15\n", + "numeric_filter = Num(\"age\").between(15, 35)\n", "\n", "v.set_filter(numeric_filter)\n", "result_print(index.query(v))" @@ -393,6 +395,132 @@ "result_print(index.query(v))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Timestamp Filters\n", + "\n", + "In redis all times are stored as an epoch time numeric however, this class allows you to filter with python datetime for ease of use. " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch comparison: 1742147139.132589\n" + ] + }, + { + "data": { + "text/html": [ + "
vector_distanceusercredit_scoreagejoboffice_locationlast_updated
0.109129190445tylerhigh100engineer-122.0839,37.38611742232589
0.217882037163taimurlow15CEO-122.0839,37.38611742232589
0.653301358223joemedium35dentist-122.0839,37.38611742232589
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from redisvl.query.filter import Timestamp\n", + "from datetime import datetime\n", + "\n", + "dt = datetime(2025, 3, 16, 13, 45, 39, 132589)\n", + "print(f'Epoch comparison: {dt.timestamp()}')\n", + "\n", + "timestamp_filter = Timestamp(\"last_updated\") > dt\n", + "\n", + "v.set_filter(timestamp_filter)\n", + "result_print(index.query(v))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch comparison: 1742147139.132589\n" + ] + }, + { + "data": { + "text/html": [ + "
vector_distanceusercredit_scoreagejoboffice_locationlast_updated
0derricklow14doctor-122.4194,37.77491741627789
0johnhigh18engineer-122.4194,37.77491741627789
0.158808946609timhigh12dermatologist-122.0839,37.38611739644189
0.266666650772nancyhigh94doctor-122.4194,37.77491710696589
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from redisvl.query.filter import Timestamp\n", + "from datetime import datetime\n", + "\n", + "dt = datetime(2025, 3, 16, 13, 45, 39, 132589)\n", + "\n", + "print(f'Epoch comparison: {dt.timestamp()}')\n", + "\n", + "timestamp_filter = Timestamp(\"last_updated\") < dt\n", + "\n", + "v.set_filter(timestamp_filter)\n", + "result_print(index.query(v))" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch between: 1736880339.132589 - 1742147139.132589\n" + ] + }, + { + "data": { + "text/html": [ + "
vector_distanceusercredit_scoreagejoboffice_locationlast_updated
0derricklow14doctor-122.4194,37.77491741627789
0johnhigh18engineer-122.4194,37.77491741627789
0.158808946609timhigh12dermatologist-122.0839,37.38611739644189
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from redisvl.query.filter import Timestamp\n", + "from datetime import datetime\n", + "\n", + "dt_1 = datetime(2025, 1, 14, 13, 45, 39, 132589)\n", + "dt_2 = datetime(2025, 3, 16, 13, 45, 39, 132589)\n", + "\n", + "print(f'Epoch between: {dt_1.timestamp()} - {dt_2.timestamp()}')\n", + "\n", + "timestamp_filter = Timestamp(\"last_updated\").between(dt_1, dt_2)\n", + "\n", + "v.set_filter(timestamp_filter)\n", + "result_print(index.query(v))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -404,13 +532,13 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
vector_distanceusercredit_scoreagejoboffice_location
0derricklow14doctor-122.4194,37.7749
0derricklow14doctor-122.4194,37.7749
0.266666650772nancyhigh94doctor-122.4194,37.7749
0.266666650772nancyhigh94doctor-122.4194,37.7749
" + "
vector_distanceusercredit_scoreagejoboffice_locationlast_updated
0derricklow14doctor-122.4194,37.77491741627789
0.266666650772nancyhigh94doctor-122.4194,37.77491710696589
" ], "text/plain": [ "" @@ -771,13 +899,13 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
vector_distanceusercredit_scoreagejoboffice_location
0johnhigh18engineer-122.4194,37.7749
0johnhigh18engineer-122.4194,37.7749
0.109129190445tylerhigh100engineer-122.0839,37.3861
0.109129190445tylerhigh100engineer-122.0839,37.3861
0.266666650772nancyhigh94doctor-122.4194,37.7749
0.266666650772nancyhigh94doctor-122.4194,37.7749
" + "
vector_distanceusercredit_scoreagejoboffice_location
0.109129190445tylerhigh100engineer-122.0839,37.3861
" ], "text/plain": [ "" @@ -791,8 +919,9 @@ "t = Tag(\"credit_score\") == \"high\"\n", "low = Num(\"age\") >= 18\n", "high = Num(\"age\") <= 100\n", + "ts = Timestamp(\"last_updated\") > datetime(2025, 3, 16, 13, 45, 39, 132589)\n", "\n", - "combined = t & low & high\n", + "combined = t & low & high & ts\n", "\n", "v = VectorQuery([0.1, 0.1, 0.5],\n", " \"user_embedding\",\n", @@ -814,13 +943,13 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
vector_distanceusercredit_scoreagejoboffice_location
0derricklow14doctor-122.4194,37.7749
0derricklow14doctor-122.4194,37.7749
0.109129190445tylerhigh100engineer-122.0839,37.3861
0.109129190445tylerhigh100engineer-122.0839,37.3861
0.158808946609timhigh12dermatologist-122.0839,37.3861
0.158808946609timhigh12dermatologist-122.0839,37.3861
0.217882037163taimurlow15CEO-122.0839,37.3861
0.217882037163taimurlow15CEO-122.0839,37.3861
0.266666650772nancyhigh94doctor-122.4194,37.7749
0.266666650772nancyhigh94doctor-122.4194,37.7749
" + "
vector_distanceusercredit_scoreagejoboffice_location
0derricklow14doctor-122.4194,37.7749
0.109129190445tylerhigh100engineer-122.0839,37.3861
0.158808946609timhigh12dermatologist-122.0839,37.3861
0.217882037163taimurlow15CEO-122.0839,37.3861
0.266666650772nancyhigh94doctor-122.4194,37.7749
" ], "text/plain": [ "" @@ -1325,7 +1454,7 @@ ], "metadata": { "kernelspec": { - "display_name": "env", + "display_name": "redisvl-Q9FZQJWe-py3.11", "language": "python", "name": "python3" }, @@ -1339,7 +1468,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.11" + "version": "3.11.9" }, "orig_nbformat": 4 }, diff --git a/docs/user_guide/09_threshold_optimization.ipynb b/docs/user_guide/09_threshold_optimization.ipynb new file mode 100644 index 00000000..2dbac38f --- /dev/null +++ b/docs/user_guide/09_threshold_optimization.ipynb @@ -0,0 +1,418 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Threshold Optimization\n", + "\n", + "After setting up `SemanticRouter` or `SemanticCache` it's best to tune the `distance_threshold` to get the most performance out of your system. RedisVL provides helper classes to make this light weight optimization easy.\n", + "\n", + "> **Note:** Threshold optimization relies on `python > 3.9.`\n", + "\n", + "# CacheThresholdOptimizer\n", + "\n", + "Let's say you setup the following semantic cache with a distance_threshold of `X` and store the entries:\n", + "\n", + "- prompt: `what is the capital of france?` response: `paris`\n", + "- prompt: `what is the capital of morocco?` response: `rabat`" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from redisvl.extensions.llmcache import SemanticCache\n", + "\n", + "sem_cache = SemanticCache(\n", + " name=\"sem_cache\", # underlying search index name\n", + " redis_url=\"redis://localhost:6379\", # redis connection url string\n", + " distance_threshold=0.5 # semantic cache distance threshold\n", + ")\n", + "\n", + "paris_key = sem_cache.store(prompt=\"what is the capital of france?\", response=\"paris\")\n", + "rabat_key = sem_cache.store(prompt=\"what is the capital of morocco?\", response=\"rabat\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This works well but we want to make sure the cache only applies for the appropriate questions. If we test the cache with a question we don't want a response to we see that the current distance_threshold is too high. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'entry_id': 'c990cc06e5e77570e5f03360426d2b7f947cbb5a67daa8af8164bfe0b3e24fe3',\n", + " 'prompt': 'what is the capital of france?',\n", + " 'response': 'paris',\n", + " 'vector_distance': 0.421104669571,\n", + " 'inserted_at': 1741039231.99,\n", + " 'updated_at': 1741039231.99,\n", + " 'key': 'sem_cache:c990cc06e5e77570e5f03360426d2b7f947cbb5a67daa8af8164bfe0b3e24fe3'}]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sem_cache.check(\"what's the capital of britain?\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Define test_data and optimize\n", + "\n", + "With the `CacheThresholdOptimizer` you can quickly tune the distance threshold by providing some test data in the form:\n", + "\n", + "```json\n", + "[\n", + " {\n", + " \"query\": \"What's the capital of Britain?\",\n", + " \"query_match\": \"\"\n", + " },\n", + " {\n", + " \"query\": \"What's the capital of France??\",\n", + " \"query_match\": paris_key\n", + " },\n", + " {\n", + " \"query\": \"What's the capital city of Morocco?\",\n", + " \"query_match\": rabat_key\n", + " },\n", + "]\n", + "```\n", + "\n", + "The threshold optimizer will then efficiently execute and score different threshold against the what is currently populated in your cache and automatically update the threshold of the cache to the best setting" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Distance threshold before: 0.5 \n", + "\n", + "Distance threshold after: 0.13050847457627118 \n", + "\n" + ] + } + ], + "source": [ + "from redisvl.utils.optimize import CacheThresholdOptimizer\n", + "\n", + "test_data = [\n", + " {\n", + " \"query\": \"What's the capital of Britain?\",\n", + " \"query_match\": \"\"\n", + " },\n", + " {\n", + " \"query\": \"What's the capital of France??\",\n", + " \"query_match\": paris_key\n", + " },\n", + " {\n", + " \"query\": \"What's the capital city of Morocco?\",\n", + " \"query_match\": rabat_key\n", + " },\n", + "]\n", + "\n", + "print(f\"Distance threshold before: {sem_cache.distance_threshold} \\n\")\n", + "optimizer = CacheThresholdOptimizer(sem_cache, test_data)\n", + "optimizer.optimize()\n", + "print(f\"Distance threshold after: {sem_cache.distance_threshold} \\n\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also see that we no longer match on the incorrect example:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sem_cache.check(\"what's the capital of britain?\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But still match on highly relevant prompts:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'entry_id': 'c990cc06e5e77570e5f03360426d2b7f947cbb5a67daa8af8164bfe0b3e24fe3',\n", + " 'prompt': 'what is the capital of france?',\n", + " 'response': 'paris',\n", + " 'vector_distance': 0.0835866332054,\n", + " 'inserted_at': 1741039231.99,\n", + " 'updated_at': 1741039231.99,\n", + " 'key': 'sem_cache:c990cc06e5e77570e5f03360426d2b7f947cbb5a67daa8af8164bfe0b3e24fe3'}]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sem_cache.check(\"what's the capital city of france?\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# RouterThresholdOptimizer\n", + "\n", + "Very similar to the caching case, you can optimize your router.\n", + "\n", + "### Define the routes" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "from redisvl.extensions.router import Route\n", + "\n", + "routes = [\n", + " Route(\n", + " name=\"greeting\",\n", + " references=[\"hello\", \"hi\"],\n", + " metadata={\"type\": \"greeting\"},\n", + " distance_threshold=0.5,\n", + " ),\n", + " Route(\n", + " name=\"farewell\",\n", + " references=[\"bye\", \"goodbye\"],\n", + " metadata={\"type\": \"farewell\"},\n", + " distance_threshold=0.5,\n", + " ),\n", + " ]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Initialize the SemanticRouter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from redisvl.extensions.router import SemanticRouter\n", + "from redisvl.utils.vectorize import HFTextVectorizer\n", + "\n", + "os.environ[\"TOKENIZERS_PARALLELISM\"] = \"false\"\n", + "\n", + "# Initialize the SemanticRouter\n", + "router = SemanticRouter(\n", + " name=\"greeting-router\",\n", + " vectorizer=HFTextVectorizer(),\n", + " routes=routes,\n", + " redis_url=\"redis://localhost:6379\",\n", + " overwrite=True # Blow away any other routing index with this name\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Provide test_data" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "test_data = [\n", + " # Greetings\n", + " {\"query\": \"hello\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"hi\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"hey\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"greetings\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"good morning\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"good afternoon\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"good evening\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"howdy\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"what's up\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"yo\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"hiya\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"salutations\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"how's it going\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"how are you\", \"query_match\": \"greeting\"},\n", + " {\"query\": \"nice to meet you\", \"query_match\": \"greeting\"},\n", + " # Farewells\n", + " {\"query\": \"goodbye\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"bye\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"see you later\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"take care\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"farewell\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"have a good day\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"see you soon\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"catch you later\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"so long\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"peace out\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"later\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"all the best\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"take it easy\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"have a good one\", \"query_match\": \"farewell\"},\n", + " {\"query\": \"cheerio\", \"query_match\": \"farewell\"},\n", + " # Null matches\n", + " {\"query\": \"what's the capital of britain?\", \"query_match\": \"\"},\n", + " {\"query\": \"what does laffy taffy taste like?\", \"query_match\": \"\"},\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Optimize\n", + "\n", + "Note: by default route distance threshold optimization will use a random search to find the best threshold since, unlike caching, there are many thresholds to optimize concurrently. " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Route thresholds before: {'greeting': 0.5, 'farewell': 0.5} \n", + "\n", + "Eval metric F1: start 0.438, end 0.719 \n", + "Ending thresholds: {'greeting': 1.0858585858585856, 'farewell': 0.5545454545454545}\n" + ] + } + ], + "source": [ + "from redisvl.utils.optimize import RouterThresholdOptimizer\n", + "\n", + "print(f\"Route thresholds before: {router.route_thresholds} \\n\")\n", + "optimizer = RouterThresholdOptimizer(router, test_data)\n", + "optimizer.optimize()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Test it out" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "RouteMatch(name='greeting', distance=0.295984119177)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Query the router with a statement\n", + "route_match = router(\"hi there\")\n", + "route_match" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cleanup" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "router.delete()\n", + "sem_cache.delete()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "redisvl-Q9FZQJWe-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/user_guide/hybrid_example_data.pkl b/docs/user_guide/hybrid_example_data.pkl index b5928b91..2c8a92da 100644 Binary files a/docs/user_guide/hybrid_example_data.pkl and b/docs/user_guide/hybrid_example_data.pkl differ diff --git a/docs/user_guide/index.md b/docs/user_guide/index.md index b6592e3d..30a51b8a 100644 --- a/docs/user_guide/index.md +++ b/docs/user_guide/index.md @@ -2,7 +2,7 @@ myst: html_meta: "description lang=en": | - User Guides for RedisVL + User guides for RedisVL --- # User Guides @@ -20,4 +20,6 @@ User guides provide helpful resources for using RedisVL and its different compon 06_rerankers 07_session_manager 08_semantic_router +09_threshold_optimization +release_guide/index ``` diff --git a/docs/user_guide/release_guide/0_5_0_release.ipynb b/docs/user_guide/release_guide/0_5_0_release.ipynb new file mode 100644 index 00000000..ca539f34 --- /dev/null +++ b/docs/user_guide/release_guide/0_5_0_release.ipynb @@ -0,0 +1,715 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 0.5.0 Feature Overview\n", + "\n", + "This notebook provides an overview of what's new with the 0.5.0 release of redisvl. It also highlights changes and potential enhancements for existing usage.\n", + "\n", + "## What's new?\n", + "\n", + "- Hybrid query and text query classes\n", + "- Threshold optimizer classes\n", + "- Schema validation\n", + "- Timestamp filters\n", + "- Batched queries\n", + "- Vector normalization\n", + "- Hybrid policy on knn with filters\n", + "\n", + "## Define and load index for examples" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[32m12:44:52\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mINFO\u001b[0m Index already exists, overwriting.\n" + ] + }, + { + "data": { + "text/plain": [ + "['jobs:01JR0V1SA29RVD9AAVSTBV9P5H',\n", + " 'jobs:01JR0V1SA209KMVHMD7G54P3H5',\n", + " 'jobs:01JR0V1SA23ZE7BRERXTZWC33Z']" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from redisvl.utils.vectorize import HFTextVectorizer\n", + "from redisvl.index import SearchIndex\n", + "import datetime as dt\n", + "\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\", category=UserWarning, module=\"redis\")\n", + "\n", + "# Embedding model\n", + "emb_model = HFTextVectorizer()\n", + "\n", + "REDIS_URL = \"redis://localhost:6379/0\"\n", + "NOW = dt.datetime.now()\n", + "\n", + "job_data = [\n", + " {\n", + " \"job_title\": \"Software Engineer\",\n", + " \"job_description\": \"Develop and maintain web applications using JavaScript, React, and Node.js.\",\n", + " \"posted\": (NOW - dt.timedelta(days=1)).timestamp() # day ago\n", + " },\n", + " {\n", + " \"job_title\": \"Data Analyst\",\n", + " \"job_description\": \"Analyze large datasets to provide business insights and create data visualizations.\",\n", + " \"posted\": (NOW - dt.timedelta(days=7)).timestamp() # week ago\n", + " },\n", + " {\n", + " \"job_title\": \"Marketing Manager\",\n", + " \"job_description\": \"Develop and implement marketing strategies to drive brand awareness and customer engagement.\",\n", + " \"posted\": (NOW - dt.timedelta(days=30)).timestamp() # month ago\n", + " }\n", + "]\n", + "\n", + "job_data = [{**job, \"job_embedding\": emb_model.embed(job[\"job_description\"], as_buffer=True)} for job in job_data]\n", + "\n", + "\n", + "job_schema = {\n", + " \"index\": {\n", + " \"name\": \"jobs\",\n", + " \"prefix\": \"jobs\",\n", + " \"storage_type\": \"hash\",\n", + " },\n", + " \"fields\": [\n", + " {\"name\": \"job_title\", \"type\": \"text\"},\n", + " {\"name\": \"job_description\", \"type\": \"text\"},\n", + " {\"name\": \"posted\", \"type\": \"numeric\"},\n", + " {\n", + " \"name\": \"job_embedding\",\n", + " \"type\": \"vector\",\n", + " \"attrs\": {\n", + " \"dims\": 768,\n", + " \"distance_metric\": \"cosine\",\n", + " \"algorithm\": \"flat\",\n", + " \"datatype\": \"float32\"\n", + " }\n", + "\n", + " }\n", + " ],\n", + "}\n", + "\n", + "index = SearchIndex.from_dict(job_schema, redis_url=REDIS_URL)\n", + "index.create(overwrite=True, drop=True)\n", + "index.load(job_data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# HybridQuery class\n", + "\n", + "Perform hybrid lexical (BM25) and vector search where results are ranked by: `hybrid_score = (1-alpha)*lexical_Score + alpha*vector_similarity`." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'vector_distance': '0.61871612072',\n", + " 'job_title': 'Software Engineer',\n", + " 'vector_similarity': '0.69064193964',\n", + " 'text_score': '49.6242910712',\n", + " 'hybrid_score': '15.3707366791'},\n", + " {'vector_distance': '0.937997639179',\n", + " 'job_title': 'Marketing Manager',\n", + " 'vector_similarity': '0.53100118041',\n", + " 'text_score': '49.6242910712',\n", + " 'hybrid_score': '15.2589881476'},\n", + " {'vector_distance': '0.859166145325',\n", + " 'job_title': 'Data Analyst',\n", + " 'vector_similarity': '0.570416927338',\n", + " 'text_score': '0',\n", + " 'hybrid_score': '0.399291849136'}]" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from redisvl.query import HybridQuery\n", + "\n", + "text = \"Find a job as a where you develop software\"\n", + "vec = emb_model.embed(text, as_buffer=True)\n", + "\n", + "query = HybridQuery(\n", + " text=text,\n", + " text_field_name=\"job_description\",\n", + " vector=vec,\n", + " vector_field_name=\"job_embedding\",\n", + " alpha=0.7,\n", + " num_results=10,\n", + " return_fields=[\"job_title\"],\n", + ")\n", + "\n", + "results = index.query(query)\n", + "results" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# TextQueries\n", + "\n", + "TextQueries make it easy to perform pure lexical search with redisvl." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 'jobs:01JR0V1SA29RVD9AAVSTBV9P5H',\n", + " 'score': 49.62429107116745,\n", + " 'job_title': 'Software Engineer'},\n", + " {'id': 'jobs:01JR0V1SA23ZE7BRERXTZWC33Z',\n", + " 'score': 49.62429107116745,\n", + " 'job_title': 'Marketing Manager'}]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from redisvl.query import TextQuery\n", + "\n", + "text = \"Find where you develop software\"\n", + "\n", + "query = TextQuery(\n", + " text=text,\n", + " text_field_name=\"job_description\",\n", + " return_fields=[\"job_title\"],\n", + " num_results=10,\n", + ")\n", + "\n", + "results = index.query(query)\n", + "results" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Threshold optimization\n", + "\n", + "In redis 0.5.0 we added the ability to quickly configure either your semantic cache or semantic router with test data examples.\n", + "\n", + "For a step by step guide see: [09_threshold_optimization.ipynb](../09_threshold_optimization.ipynb).\n", + "\n", + "For a more advanced routing example see: [this example](https://github.com/redis-developer/redis-ai-resources/blob/main/python-recipes/semantic-router/01_routing_optimization.ipynb). " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Distance threshold before: 0.5 \n", + "\n", + "\n", + "Distance threshold after: 0.13050847457627118 \n", + "\n" + ] + } + ], + "source": [ + "from redisvl.utils.optimize import CacheThresholdOptimizer\n", + "from redisvl.extensions.llmcache import SemanticCache\n", + "\n", + "sem_cache = SemanticCache(\n", + " name=\"sem_cache\", # underlying search index name\n", + " redis_url=\"redis://localhost:6379\", # redis connection url string\n", + " distance_threshold=0.5 # semantic cache distance threshold\n", + ")\n", + "\n", + "paris_key = sem_cache.store(prompt=\"what is the capital of france?\", response=\"paris\")\n", + "rabat_key = sem_cache.store(prompt=\"what is the capital of morocco?\", response=\"rabat\")\n", + "\n", + "test_data = [\n", + " {\n", + " \"query\": \"What's the capital of Britain?\",\n", + " \"query_match\": \"\"\n", + " },\n", + " {\n", + " \"query\": \"What's the capital of France??\",\n", + " \"query_match\": paris_key\n", + " },\n", + " {\n", + " \"query\": \"What's the capital city of Morocco?\",\n", + " \"query_match\": rabat_key\n", + " },\n", + "]\n", + "\n", + "print(f\"\\nDistance threshold before: {sem_cache.distance_threshold} \\n\")\n", + "optimizer = CacheThresholdOptimizer(sem_cache, test_data)\n", + "optimizer.optimize()\n", + "print(f\"\\nDistance threshold after: {sem_cache.distance_threshold} \\n\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Schema validation\n", + "\n", + "This feature makes it easier to make sure your data is in the right format. To demo this we will create a new index with the `validate_on_load` flag set to `True`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[32m16:20:25\u001b[0m \u001b[34mredisvl.index.index\u001b[0m \u001b[1;30mERROR\u001b[0m \u001b[31mSchema validation error while loading data\u001b[0m\n", + "Traceback (most recent call last):\n", + " File \"/Users/robert.shelton/.pyenv/versions/3.11.9/lib/python3.11/site-packages/redisvl/index/storage.py\", line 204, in _preprocess_and_validate_objects\n", + " processed_obj = self._validate(processed_obj)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/Users/robert.shelton/.pyenv/versions/3.11.9/lib/python3.11/site-packages/redisvl/index/storage.py\", line 160, in _validate\n", + " return validate_object(self.index_schema, obj)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/Users/robert.shelton/.pyenv/versions/3.11.9/lib/python3.11/site-packages/redisvl/schema/validation.py\", line 276, in validate_object\n", + " validated = model_class.model_validate(flat_obj)\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/Users/robert.shelton/.pyenv/versions/3.11.9/lib/python3.11/site-packages/pydantic/main.py\", line 627, in model_validate\n", + " return cls.__pydantic_validator__.validate_python(\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + "pydantic_core._pydantic_core.ValidationError: 2 validation errors for cars__PydanticModel\n", + "mpg.int\n", + " Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='twenty-two', input_type=str]\n", + " For further information visit https://errors.pydantic.dev/2.10/v/int_parsing\n", + "mpg.float\n", + " Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='twenty-two', input_type=str]\n", + " For further information visit https://errors.pydantic.dev/2.10/v/float_parsing\n", + "\n", + "The above exception was the direct cause of the following exception:\n", + "\n", + "Traceback (most recent call last):\n", + " File \"/Users/robert.shelton/.pyenv/versions/3.11.9/lib/python3.11/site-packages/redisvl/index/index.py\", line 615, in load\n", + " return self._storage.write(\n", + " ^^^^^^^^^^^^^^^^^^^^\n", + " File \"/Users/robert.shelton/.pyenv/versions/3.11.9/lib/python3.11/site-packages/redisvl/index/storage.py\", line 265, in write\n", + " prepared_objects = self._preprocess_and_validate_objects(\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"/Users/robert.shelton/.pyenv/versions/3.11.9/lib/python3.11/site-packages/redisvl/index/storage.py\", line 211, in _preprocess_and_validate_objects\n", + " raise SchemaValidationError(str(e), index=i) from e\n", + "redisvl.exceptions.SchemaValidationError: Validation failed for object at index 1: 2 validation errors for cars__PydanticModel\n", + "mpg.int\n", + " Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='twenty-two', input_type=str]\n", + " For further information visit https://errors.pydantic.dev/2.10/v/int_parsing\n", + "mpg.float\n", + " Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='twenty-two', input_type=str]\n", + " For further information visit https://errors.pydantic.dev/2.10/v/float_parsing\n", + "Error loading data: Validation failed for object at index 1: 2 validation errors for cars__PydanticModel\n", + "mpg.int\n", + " Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='twenty-two', input_type=str]\n", + " For further information visit https://errors.pydantic.dev/2.10/v/int_parsing\n", + "mpg.float\n", + " Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='twenty-two', input_type=str]\n", + " For further information visit https://errors.pydantic.dev/2.10/v/float_parsing\n" + ] + } + ], + "source": [ + "# NBVAL_SKIP\n", + "from redisvl.index import SearchIndex\n", + "\n", + "# sample schema\n", + "car_schema = {\n", + " \"index\": {\n", + " \"name\": \"cars\",\n", + " \"prefix\": \"cars\",\n", + " \"storage_type\": \"json\",\n", + " },\n", + " \"fields\": [\n", + " {\"name\": \"make\", \"type\": \"text\"},\n", + " {\"name\": \"model\", \"type\": \"text\"},\n", + " {\"name\": \"description\", \"type\": \"text\"},\n", + " {\"name\": \"mpg\", \"type\": \"numeric\"},\n", + " {\n", + " \"name\": \"car_embedding\",\n", + " \"type\": \"vector\",\n", + " \"attrs\": {\n", + " \"dims\": 3,\n", + " \"distance_metric\": \"cosine\",\n", + " \"algorithm\": \"flat\",\n", + " \"datatype\": \"float32\"\n", + " }\n", + "\n", + " }\n", + " ],\n", + "}\n", + "\n", + "sample_data_bad = [\n", + " {\n", + " \"make\": \"Toyota\",\n", + " \"model\": \"Camry\",\n", + " \"description\": \"A reliable sedan with great fuel economy.\",\n", + " \"mpg\": 28,\n", + " \"car_embedding\": [0.1, 0.2, 0.3]\n", + " },\n", + " {\n", + " \"make\": \"Honda\",\n", + " \"model\": \"CR-V\",\n", + " \"description\": \"A practical SUV with advanced technology.\",\n", + " # incorrect type will throw an error\n", + " \"mpg\": \"twenty-two\",\n", + " \"car_embedding\": [0.4, 0.5, 0.6]\n", + " }\n", + "]\n", + "\n", + "# this should now throw an error\n", + "car_index = SearchIndex.from_dict(car_schema, redis_url=REDIS_URL, validate_on_load=True)\n", + "car_index.create(overwrite=True)\n", + "\n", + "try:\n", + " car_index.load(sample_data_bad)\n", + "except Exception as e:\n", + " print(f\"Error loading data: {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Timestamp filters\n", + "\n", + "In Redis datetime objects are stored as numeric epoch times. Timestamp filter makes it easier to handle querying by these fields by handling conversion for you." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 'jobs:01JQYMYZBA6NM6DX9YW35MCHJZ',\n", + " 'job_title': 'Software Engineer',\n", + " 'job_description': 'Develop and maintain web applications using JavaScript, React, and Node.js.',\n", + " 'posted': '1743625199.9'},\n", + " {'id': 'jobs:01JQYMYZBABXYR96H96SQ99ZPS',\n", + " 'job_title': 'Data Analyst',\n", + " 'job_description': 'Analyze large datasets to provide business insights and create data visualizations.',\n", + " 'posted': '1743106799.9'},\n", + " {'id': 'jobs:01JQYMYZBAGEBDS270EZADQ1TM',\n", + " 'job_title': 'Marketing Manager',\n", + " 'job_description': 'Develop and implement marketing strategies to drive brand awareness and customer engagement.',\n", + " 'posted': '1741123199.9'}]" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from redisvl.query import FilterQuery\n", + "from redisvl.query.filter import Timestamp\n", + "\n", + "# find all jobs\n", + "ts = Timestamp(\"posted\") < NOW # now datetime created above\n", + "\n", + "filter_query = FilterQuery(\n", + " return_fields=[\"job_title\", \"job_description\", \"posted\"], \n", + " filter_expression=ts,\n", + " num_results=10,\n", + ")\n", + "res = index.query(filter_query)\n", + "res" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 'jobs:01JQYMYZBA6NM6DX9YW35MCHJZ',\n", + " 'job_title': 'Software Engineer',\n", + " 'job_description': 'Develop and maintain web applications using JavaScript, React, and Node.js.',\n", + " 'posted': '1743625199.9'}]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# jobs posted in the last 3 days => 1 job\n", + "ts = Timestamp(\"posted\") > NOW - dt.timedelta(days=3)\n", + "\n", + "filter_query = FilterQuery(\n", + " return_fields=[\"job_title\", \"job_description\", \"posted\"], \n", + " filter_expression=ts,\n", + " num_results=10,\n", + ")\n", + "res = index.query(filter_query)\n", + "res" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 'jobs:01JQYMYZBABXYR96H96SQ99ZPS',\n", + " 'job_title': 'Data Analyst',\n", + " 'job_description': 'Analyze large datasets to provide business insights and create data visualizations.',\n", + " 'posted': '1743106799.9'}]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# more than 3 days ago but less than 14 days ago => 1 job\n", + "ts = Timestamp(\"posted\").between(\n", + " NOW - dt.timedelta(days=14),\n", + " NOW - dt.timedelta(days=3),\n", + ")\n", + "\n", + "filter_query = FilterQuery(\n", + " return_fields=[\"job_title\", \"job_description\", \"posted\"], \n", + " filter_expression=ts,\n", + " num_results=10,\n", + ")\n", + "\n", + "res = index.query(filter_query)\n", + "res" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Batch search\n", + "\n", + "This enhancement allows you to speed up the execution of queries by reducing the impact of network latency." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Time taken for 200 queries: 0.11 seconds\n" + ] + } + ], + "source": [ + "import time\n", + "num_queries = 200\n", + "\n", + "start = time.time()\n", + "for i in range(num_queries):\n", + " # run the same filter query \n", + " res = index.query(filter_query)\n", + "end = time.time()\n", + "print(f\"Time taken for {num_queries} queries: {end - start:.2f} seconds\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Time taken for 200 batched queries: 0.03 seconds\n" + ] + } + ], + "source": [ + "batched_queries = [filter_query] * num_queries\n", + "\n", + "start = time.time()\n", + "\n", + "index.batch_search(batched_queries, batch_size=10)\n", + "\n", + "end = time.time()\n", + "print(f\"Time taken for {num_queries} batched queries: {end - start:.2f} seconds\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Vector normalization\n", + "\n", + "By default, Redis returns the vector cosine distance when performing a search, which yields a value between 0 and 2, where 0 represents a perfect match. However, you may sometimes prefer a similarity score between 0 and 1, where 1 indicates a perfect match. When enabled, this flag performs the conversion for you. Additionally, if this flag is set to true for L2 distance, it normalizes the Euclidean distance to a value between 0 and 1 as well.\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 'jobs:01JQYMYZBA6NM6DX9YW35MCHJZ',\n", + " 'vector_distance': '0.7090711295605',\n", + " 'job_title': 'Software Engineer',\n", + " 'job_description': 'Develop and maintain web applications using JavaScript, React, and Node.js.',\n", + " 'posted': '1743625199.9'},\n", + " {'id': 'jobs:01JQYMYZBABXYR96H96SQ99ZPS',\n", + " 'vector_distance': '0.6049451231955',\n", + " 'job_title': 'Data Analyst',\n", + " 'job_description': 'Analyze large datasets to provide business insights and create data visualizations.',\n", + " 'posted': '1743106799.9'},\n", + " {'id': 'jobs:01JQYMYZBAGEBDS270EZADQ1TM',\n", + " 'vector_distance': '0.553376108408',\n", + " 'job_title': 'Marketing Manager',\n", + " 'job_description': 'Develop and implement marketing strategies to drive brand awareness and customer engagement.',\n", + " 'posted': '1741123199.9'}]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from redisvl.query import VectorQuery\n", + "\n", + "query = VectorQuery(\n", + " vector=emb_model.embed(\"Software Engineer\", as_buffer=True),\n", + " vector_field_name=\"job_embedding\",\n", + " return_fields=[\"job_title\", \"job_description\", \"posted\"],\n", + " normalize_vector_distance=True,\n", + ")\n", + "\n", + "res = index.query(query)\n", + "res" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Hybrid policy on knn with filters\n", + "\n", + "Within the default redis client you can set the `HYBRID_POLICY` which specifies the filter mode to use during vector search with filters. It can take values `BATCHES` or `ADHOC_BF`. Previously this option was not exposed by redisvl." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 'jobs:01JQYMYZBA6NM6DX9YW35MCHJZ',\n", + " 'vector_distance': '0.581857740879',\n", + " 'job_title': 'Software Engineer',\n", + " 'job_description': 'Develop and maintain web applications using JavaScript, React, and Node.js.',\n", + " 'posted': '1743625199.9'},\n", + " {'id': 'jobs:01JQYMYZBAGEBDS270EZADQ1TM',\n", + " 'vector_distance': '0.893247783184',\n", + " 'job_title': 'Marketing Manager',\n", + " 'job_description': 'Develop and implement marketing strategies to drive brand awareness and customer engagement.',\n", + " 'posted': '1741123199.9'}]" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from redisvl.query.filter import Text\n", + "\n", + "filter = Text(\"job_description\") % \"Develop\"\n", + "\n", + "query = VectorQuery(\n", + " vector=emb_model.embed(\"Software Engineer\", as_buffer=True),\n", + " vector_field_name=\"job_embedding\",\n", + " return_fields=[\"job_title\", \"job_description\", \"posted\"],\n", + " hybrid_policy=\"BATCHES\"\n", + ")\n", + "\n", + "query.set_filter(filter)\n", + "\n", + "res = index.query(query)\n", + "res" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "redisvl-56gG2io_-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/user_guide/release_guide/index.md b/docs/user_guide/release_guide/index.md new file mode 100644 index 00000000..1eba08c9 --- /dev/null +++ b/docs/user_guide/release_guide/index.md @@ -0,0 +1,17 @@ +--- +myst: + html_meta: + "description lang=en": | + Release guides for RedisVL +--- + +# Release Guides + +This section contains guidelines and information for RedisVL releases. + +```{toctree} +:caption: Release Guides +:maxdepth: 2 + +0_5_0_release +``` diff --git a/docs/user_guide/router.yaml b/docs/user_guide/router.yaml index 38aecfdd..b743aaf6 100644 --- a/docs/user_guide/router.yaml +++ b/docs/user_guide/router.yaml @@ -7,7 +7,7 @@ routes: - what's trending in tech? metadata: category: tech - priority: '1' + priority: 1 distance_threshold: 1.0 - name: sports references: @@ -18,7 +18,7 @@ routes: - basketball and football metadata: category: sports - priority: '2' + priority: 2 distance_threshold: 0.5 - name: entertainment references: @@ -27,12 +27,11 @@ routes: - what's new in the entertainment industry? metadata: category: entertainment - priority: '3' + priority: 3 distance_threshold: 0.7 vectorizer: type: hf model: sentence-transformers/all-mpnet-base-v2 routing_config: - distance_threshold: 0.5 max_k: 3 aggregation_method: min diff --git a/poetry.lock b/poetry.lock index a86a3523..16d7cd16 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "accessible-pygments" @@ -34,94 +34,94 @@ files = [ [[package]] name = "aiohttp" -version = "3.11.12" +version = "3.11.13" description = "Async http client/server framework (asyncio)" optional = true python-versions = ">=3.9" groups = ["main"] markers = "extra == \"voyageai\"" files = [ - {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aa8a8caca81c0a3e765f19c6953416c58e2f4cc1b84829af01dd1c771bb2f91f"}, - {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84ede78acde96ca57f6cf8ccb8a13fbaf569f6011b9a52f870c662d4dc8cd854"}, - {file = "aiohttp-3.11.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:584096938a001378484aa4ee54e05dc79c7b9dd933e271c744a97b3b6f644957"}, - {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:392432a2dde22b86f70dd4a0e9671a349446c93965f261dbaecfaf28813e5c42"}, - {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:88d385b8e7f3a870146bf5ea31786ef7463e99eb59e31db56e2315535d811f55"}, - {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b10a47e5390c4b30a0d58ee12581003be52eedd506862ab7f97da7a66805befb"}, - {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b5263dcede17b6b0c41ef0c3ccce847d82a7da98709e75cf7efde3e9e3b5cae"}, - {file = "aiohttp-3.11.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50c5c7b8aa5443304c55c262c5693b108c35a3b61ef961f1e782dd52a2f559c7"}, - {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1c031a7572f62f66f1257db37ddab4cb98bfaf9b9434a3b4840bf3560f5e788"}, - {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:7e44eba534381dd2687be50cbd5f2daded21575242ecfdaf86bbeecbc38dae8e"}, - {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:145a73850926018ec1681e734cedcf2716d6a8697d90da11284043b745c286d5"}, - {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2c311e2f63e42c1bf86361d11e2c4a59f25d9e7aabdbdf53dc38b885c5435cdb"}, - {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ea756b5a7bac046d202a9a3889b9a92219f885481d78cd318db85b15cc0b7bcf"}, - {file = "aiohttp-3.11.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:526c900397f3bbc2db9cb360ce9c35134c908961cdd0ac25b1ae6ffcaa2507ff"}, - {file = "aiohttp-3.11.12-cp310-cp310-win32.whl", hash = "sha256:b8d3bb96c147b39c02d3db086899679f31958c5d81c494ef0fc9ef5bb1359b3d"}, - {file = "aiohttp-3.11.12-cp310-cp310-win_amd64.whl", hash = "sha256:7fe3d65279bfbee8de0fb4f8c17fc4e893eed2dba21b2f680e930cc2b09075c5"}, - {file = "aiohttp-3.11.12-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87a2e00bf17da098d90d4145375f1d985a81605267e7f9377ff94e55c5d769eb"}, - {file = "aiohttp-3.11.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b34508f1cd928ce915ed09682d11307ba4b37d0708d1f28e5774c07a7674cac9"}, - {file = "aiohttp-3.11.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:936d8a4f0f7081327014742cd51d320296b56aa6d324461a13724ab05f4b2933"}, - {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de1378f72def7dfb5dbd73d86c19eda0ea7b0a6873910cc37d57e80f10d64e1"}, - {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9d45dbb3aaec05cf01525ee1a7ac72de46a8c425cb75c003acd29f76b1ffe94"}, - {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:930ffa1925393381e1e0a9b82137fa7b34c92a019b521cf9f41263976666a0d6"}, - {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8340def6737118f5429a5df4e88f440746b791f8f1c4ce4ad8a595f42c980bd5"}, - {file = "aiohttp-3.11.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4016e383f91f2814e48ed61e6bda7d24c4d7f2402c75dd28f7e1027ae44ea204"}, - {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c0600bcc1adfaaac321422d615939ef300df81e165f6522ad096b73439c0f58"}, - {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0450ada317a65383b7cce9576096150fdb97396dcfe559109b403c7242faffef"}, - {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:850ff6155371fd802a280f8d369d4e15d69434651b844bde566ce97ee2277420"}, - {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8fd12d0f989c6099e7b0f30dc6e0d1e05499f3337461f0b2b0dadea6c64b89df"}, - {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:76719dd521c20a58a6c256d058547b3a9595d1d885b830013366e27011ffe804"}, - {file = "aiohttp-3.11.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97fe431f2ed646a3b56142fc81d238abcbaff08548d6912acb0b19a0cadc146b"}, - {file = "aiohttp-3.11.12-cp311-cp311-win32.whl", hash = "sha256:e10c440d142fa8b32cfdb194caf60ceeceb3e49807072e0dc3a8887ea80e8c16"}, - {file = "aiohttp-3.11.12-cp311-cp311-win_amd64.whl", hash = "sha256:246067ba0cf5560cf42e775069c5d80a8989d14a7ded21af529a4e10e3e0f0e6"}, - {file = "aiohttp-3.11.12-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e392804a38353900c3fd8b7cacbea5132888f7129f8e241915e90b85f00e3250"}, - {file = "aiohttp-3.11.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8fa1510b96c08aaad49303ab11f8803787c99222288f310a62f493faf883ede1"}, - {file = "aiohttp-3.11.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dc065a4285307607df3f3686363e7f8bdd0d8ab35f12226362a847731516e42c"}, - {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddb31f8474695cd61fc9455c644fc1606c164b93bff2490390d90464b4655df"}, - {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dec0000d2d8621d8015c293e24589d46fa218637d820894cb7356c77eca3259"}, - {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3552fe98e90fdf5918c04769f338a87fa4f00f3b28830ea9b78b1bdc6140e0d"}, - {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dfe7f984f28a8ae94ff3a7953cd9678550dbd2a1f9bda5dd9c5ae627744c78e"}, - {file = "aiohttp-3.11.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a481a574af914b6e84624412666cbfbe531a05667ca197804ecc19c97b8ab1b0"}, - {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1987770fb4887560363b0e1a9b75aa303e447433c41284d3af2840a2f226d6e0"}, - {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a4ac6a0f0f6402854adca4e3259a623f5c82ec3f0c049374133bcb243132baf9"}, - {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c96a43822f1f9f69cc5c3706af33239489a6294be486a0447fb71380070d4d5f"}, - {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a5e69046f83c0d3cb8f0d5bd9b8838271b1bc898e01562a04398e160953e8eb9"}, - {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:68d54234c8d76d8ef74744f9f9fc6324f1508129e23da8883771cdbb5818cbef"}, - {file = "aiohttp-3.11.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9fd9dcf9c91affe71654ef77426f5cf8489305e1c66ed4816f5a21874b094b9"}, - {file = "aiohttp-3.11.12-cp312-cp312-win32.whl", hash = "sha256:0ed49efcd0dc1611378beadbd97beb5d9ca8fe48579fc04a6ed0844072261b6a"}, - {file = "aiohttp-3.11.12-cp312-cp312-win_amd64.whl", hash = "sha256:54775858c7f2f214476773ce785a19ee81d1294a6bedc5cc17225355aab74802"}, - {file = "aiohttp-3.11.12-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:413ad794dccb19453e2b97c2375f2ca3cdf34dc50d18cc2693bd5aed7d16f4b9"}, - {file = "aiohttp-3.11.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a93d28ed4b4b39e6f46fd240896c29b686b75e39cc6992692e3922ff6982b4c"}, - {file = "aiohttp-3.11.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d589264dbba3b16e8951b6f145d1e6b883094075283dafcab4cdd564a9e353a0"}, - {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5148ca8955affdfeb864aca158ecae11030e952b25b3ae15d4e2b5ba299bad2"}, - {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:525410e0790aab036492eeea913858989c4cb070ff373ec3bc322d700bdf47c1"}, - {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bd8695be2c80b665ae3f05cb584093a1e59c35ecb7d794d1edd96e8cc9201d7"}, - {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0203433121484b32646a5f5ea93ae86f3d9559d7243f07e8c0eab5ff8e3f70e"}, - {file = "aiohttp-3.11.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40cd36749a1035c34ba8d8aaf221b91ca3d111532e5ccb5fa8c3703ab1b967ed"}, - {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7442662afebbf7b4c6d28cb7aab9e9ce3a5df055fc4116cc7228192ad6cb484"}, - {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8a2fb742ef378284a50766e985804bd6adb5adb5aa781100b09befdbfa757b65"}, - {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2cee3b117a8d13ab98b38d5b6bdcd040cfb4181068d05ce0c474ec9db5f3c5bb"}, - {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f6a19bcab7fbd8f8649d6595624856635159a6527861b9cdc3447af288a00c00"}, - {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e4cecdb52aaa9994fbed6b81d4568427b6002f0a91c322697a4bfcc2b2363f5a"}, - {file = "aiohttp-3.11.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:30f546358dfa0953db92ba620101fefc81574f87b2346556b90b5f3ef16e55ce"}, - {file = "aiohttp-3.11.12-cp313-cp313-win32.whl", hash = "sha256:ce1bb21fc7d753b5f8a5d5a4bae99566386b15e716ebdb410154c16c91494d7f"}, - {file = "aiohttp-3.11.12-cp313-cp313-win_amd64.whl", hash = "sha256:f7914ab70d2ee8ab91c13e5402122edbc77821c66d2758abb53aabe87f013287"}, - {file = "aiohttp-3.11.12-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c3623053b85b4296cd3925eeb725e386644fd5bc67250b3bb08b0f144803e7b"}, - {file = "aiohttp-3.11.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:67453e603cea8e85ed566b2700efa1f6916aefbc0c9fcb2e86aaffc08ec38e78"}, - {file = "aiohttp-3.11.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6130459189e61baac5a88c10019b21e1f0c6d00ebc770e9ce269475650ff7f73"}, - {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9060addfa4ff753b09392efe41e6af06ea5dd257829199747b9f15bfad819460"}, - {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34245498eeb9ae54c687a07ad7f160053911b5745e186afe2d0c0f2898a1ab8a"}, - {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8dc0fba9a74b471c45ca1a3cb6e6913ebfae416678d90529d188886278e7f3f6"}, - {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a478aa11b328983c4444dacb947d4513cb371cd323f3845e53caeda6be5589d5"}, - {file = "aiohttp-3.11.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c160a04283c8c6f55b5bf6d4cad59bb9c5b9c9cd08903841b25f1f7109ef1259"}, - {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:edb69b9589324bdc40961cdf0657815df674f1743a8d5ad9ab56a99e4833cfdd"}, - {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ee84c2a22a809c4f868153b178fe59e71423e1f3d6a8cd416134bb231fbf6d3"}, - {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bf4480a5438f80e0f1539e15a7eb8b5f97a26fe087e9828e2c0ec2be119a9f72"}, - {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:e6b2732ef3bafc759f653a98881b5b9cdef0716d98f013d376ee8dfd7285abf1"}, - {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f752e80606b132140883bb262a457c475d219d7163d996dc9072434ffb0784c4"}, - {file = "aiohttp-3.11.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ab3247d58b393bda5b1c8f31c9edece7162fc13265334217785518dd770792b8"}, - {file = "aiohttp-3.11.12-cp39-cp39-win32.whl", hash = "sha256:0d5176f310a7fe6f65608213cc74f4228e4f4ce9fd10bcb2bb6da8fc66991462"}, - {file = "aiohttp-3.11.12-cp39-cp39-win_amd64.whl", hash = "sha256:74bd573dde27e58c760d9ca8615c41a57e719bff315c9adb6f2a4281a28e8798"}, - {file = "aiohttp-3.11.12.tar.gz", hash = "sha256:7603ca26d75b1b86160ce1bbe2787a0b706e592af5b2504e12caa88a217767b0"}, + {file = "aiohttp-3.11.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a4fe27dbbeec445e6e1291e61d61eb212ee9fed6e47998b27de71d70d3e8777d"}, + {file = "aiohttp-3.11.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9e64ca2dbea28807f8484c13f684a2f761e69ba2640ec49dacd342763cc265ef"}, + {file = "aiohttp-3.11.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9840be675de208d1f68f84d578eaa4d1a36eee70b16ae31ab933520c49ba1325"}, + {file = "aiohttp-3.11.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28a772757c9067e2aee8a6b2b425d0efaa628c264d6416d283694c3d86da7689"}, + {file = "aiohttp-3.11.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b88aca5adbf4625e11118df45acac29616b425833c3be7a05ef63a6a4017bfdb"}, + {file = "aiohttp-3.11.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce10ddfbe26ed5856d6902162f71b8fe08545380570a885b4ab56aecfdcb07f4"}, + {file = "aiohttp-3.11.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa48dac27f41b36735c807d1ab093a8386701bbf00eb6b89a0f69d9fa26b3671"}, + {file = "aiohttp-3.11.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89ce611b1eac93ce2ade68f1470889e0173d606de20c85a012bfa24be96cf867"}, + {file = "aiohttp-3.11.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:78e4dd9c34ec7b8b121854eb5342bac8b02aa03075ae8618b6210a06bbb8a115"}, + {file = "aiohttp-3.11.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:66047eacbc73e6fe2462b77ce39fc170ab51235caf331e735eae91c95e6a11e4"}, + {file = "aiohttp-3.11.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5ad8f1c19fe277eeb8bc45741c6d60ddd11d705c12a4d8ee17546acff98e0802"}, + {file = "aiohttp-3.11.13-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64815c6f02e8506b10113ddbc6b196f58dbef135751cc7c32136df27b736db09"}, + {file = "aiohttp-3.11.13-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:967b93f21b426f23ca37329230d5bd122f25516ae2f24a9cea95a30023ff8283"}, + {file = "aiohttp-3.11.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf1f31f83d16ec344136359001c5e871915c6ab685a3d8dee38e2961b4c81730"}, + {file = "aiohttp-3.11.13-cp310-cp310-win32.whl", hash = "sha256:00c8ac69e259c60976aa2edae3f13d9991cf079aaa4d3cd5a49168ae3748dee3"}, + {file = "aiohttp-3.11.13-cp310-cp310-win_amd64.whl", hash = "sha256:90d571c98d19a8b6e793b34aa4df4cee1e8fe2862d65cc49185a3a3d0a1a3996"}, + {file = "aiohttp-3.11.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b35aab22419ba45f8fc290d0010898de7a6ad131e468ffa3922b1b0b24e9d2e"}, + {file = "aiohttp-3.11.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81cba651db8795f688c589dd11a4fbb834f2e59bbf9bb50908be36e416dc760"}, + {file = "aiohttp-3.11.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f55d0f242c2d1fcdf802c8fabcff25a9d85550a4cf3a9cf5f2a6b5742c992839"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4bea08a6aad9195ac9b1be6b0c7e8a702a9cec57ce6b713698b4a5afa9c2e33"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6070bcf2173a7146bb9e4735b3c62b2accba459a6eae44deea0eb23e0035a23"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:718d5deb678bc4b9d575bfe83a59270861417da071ab44542d0fcb6faa686636"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f6b2c5b4a4d22b8fb2c92ac98e0747f5f195e8e9448bfb7404cd77e7bfa243f"}, + {file = "aiohttp-3.11.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:747ec46290107a490d21fe1ff4183bef8022b848cf9516970cb31de6d9460088"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:01816f07c9cc9d80f858615b1365f8319d6a5fd079cd668cc58e15aafbc76a54"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a08ad95fcbd595803e0c4280671d808eb170a64ca3f2980dd38e7a72ed8d1fea"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c97be90d70f7db3aa041d720bfb95f4869d6063fcdf2bb8333764d97e319b7d0"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ab915a57c65f7a29353c8014ac4be685c8e4a19e792a79fe133a8e101111438e"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:35cda4e07f5e058a723436c4d2b7ba2124ab4e0aa49e6325aed5896507a8a42e"}, + {file = "aiohttp-3.11.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:af55314407714fe77a68a9ccaab90fdb5deb57342585fd4a3a8102b6d4370080"}, + {file = "aiohttp-3.11.13-cp311-cp311-win32.whl", hash = "sha256:42d689a5c0a0c357018993e471893e939f555e302313d5c61dfc566c2cad6185"}, + {file = "aiohttp-3.11.13-cp311-cp311-win_amd64.whl", hash = "sha256:b73a2b139782a07658fbf170fe4bcdf70fc597fae5ffe75e5b67674c27434a9f"}, + {file = "aiohttp-3.11.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2eabb269dc3852537d57589b36d7f7362e57d1ece308842ef44d9830d2dc3c90"}, + {file = "aiohttp-3.11.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b77ee42addbb1c36d35aca55e8cc6d0958f8419e458bb70888d8c69a4ca833d"}, + {file = "aiohttp-3.11.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55789e93c5ed71832e7fac868167276beadf9877b85697020c46e9a75471f55f"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c929f9a7249a11e4aa5c157091cfad7f49cc6b13f4eecf9b747104befd9f56f2"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d33851d85537bbf0f6291ddc97926a754c8f041af759e0aa0230fe939168852b"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9229d8613bd8401182868fe95688f7581673e1c18ff78855671a4b8284f47bcb"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669dd33f028e54fe4c96576f406ebb242ba534dd3a981ce009961bf49960f117"}, + {file = "aiohttp-3.11.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c1b20a1ace54af7db1f95af85da530fe97407d9063b7aaf9ce6a32f44730778"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5724cc77f4e648362ebbb49bdecb9e2b86d9b172c68a295263fa072e679ee69d"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:aa36c35e94ecdb478246dd60db12aba57cfcd0abcad43c927a8876f25734d496"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9b5b37c863ad5b0892cc7a4ceb1e435e5e6acd3f2f8d3e11fa56f08d3c67b820"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e06cf4852ce8c4442a59bae5a3ea01162b8fcb49ab438d8548b8dc79375dad8a"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5194143927e494616e335d074e77a5dac7cd353a04755330c9adc984ac5a628e"}, + {file = "aiohttp-3.11.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afcb6b275c2d2ba5d8418bf30a9654fa978b4f819c2e8db6311b3525c86fe637"}, + {file = "aiohttp-3.11.13-cp312-cp312-win32.whl", hash = "sha256:7104d5b3943c6351d1ad7027d90bdd0ea002903e9f610735ac99df3b81f102ee"}, + {file = "aiohttp-3.11.13-cp312-cp312-win_amd64.whl", hash = "sha256:47dc018b1b220c48089b5b9382fbab94db35bef2fa192995be22cbad3c5730c8"}, + {file = "aiohttp-3.11.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9862d077b9ffa015dbe3ce6c081bdf35135948cb89116e26667dd183550833d1"}, + {file = "aiohttp-3.11.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fbfef0666ae9e07abfa2c54c212ac18a1f63e13e0760a769f70b5717742f3ece"}, + {file = "aiohttp-3.11.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:93a1f7d857c4fcf7cabb1178058182c789b30d85de379e04f64c15b7e88d66fb"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba40b7ae0f81c7029583a338853f6607b6d83a341a3dcde8bed1ea58a3af1df9"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5b95787335c483cd5f29577f42bbe027a412c5431f2f80a749c80d040f7ca9f"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7d474c5c1f0b9405c1565fafdc4429fa7d986ccbec7ce55bc6a330f36409cad"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e83fb1991e9d8982b3b36aea1e7ad27ea0ce18c14d054c7a404d68b0319eebb"}, + {file = "aiohttp-3.11.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4586a68730bd2f2b04a83e83f79d271d8ed13763f64b75920f18a3a677b9a7f0"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fe4eb0e7f50cdb99b26250d9328faef30b1175a5dbcfd6d0578d18456bac567"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2a8a6bc19818ac3e5596310ace5aa50d918e1ebdcc204dc96e2f4d505d51740c"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f27eec42f6c3c1df09cfc1f6786308f8b525b8efaaf6d6bd76c1f52c6511f6a"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2a4a13dfbb23977a51853b419141cd0a9b9573ab8d3a1455c6e63561387b52ff"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:02876bf2f69b062584965507b07bc06903c2dc93c57a554b64e012d636952654"}, + {file = "aiohttp-3.11.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b992778d95b60a21c4d8d4a5f15aaab2bd3c3e16466a72d7f9bfd86e8cea0d4b"}, + {file = "aiohttp-3.11.13-cp313-cp313-win32.whl", hash = "sha256:507ab05d90586dacb4f26a001c3abf912eb719d05635cbfad930bdbeb469b36c"}, + {file = "aiohttp-3.11.13-cp313-cp313-win_amd64.whl", hash = "sha256:5ceb81a4db2decdfa087381b5fc5847aa448244f973e5da232610304e199e7b2"}, + {file = "aiohttp-3.11.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:51c3ff9c7a25f3cad5c09d9aacbc5aefb9267167c4652c1eb737989b554fe278"}, + {file = "aiohttp-3.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e271beb2b1dabec5cd84eb488bdabf9758d22ad13471e9c356be07ad139b3012"}, + {file = "aiohttp-3.11.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0e9eb7e5764abcb49f0e2bd8f5731849b8728efbf26d0cac8e81384c95acec3f"}, + {file = "aiohttp-3.11.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baae005092e3f200de02699314ac8933ec20abf998ec0be39448f6605bce93df"}, + {file = "aiohttp-3.11.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1982c98ac62c132d2b773d50e2fcc941eb0b8bad3ec078ce7e7877c4d5a2dce7"}, + {file = "aiohttp-3.11.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2b25b2eeb35707113b2d570cadc7c612a57f1c5d3e7bb2b13870fe284e08fc0"}, + {file = "aiohttp-3.11.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b27961d65639128336b7a7c3f0046dcc62a9443d5ef962e3c84170ac620cec47"}, + {file = "aiohttp-3.11.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a01fe9f1e05025eacdd97590895e2737b9f851d0eb2e017ae9574d9a4f0b6252"}, + {file = "aiohttp-3.11.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa1fb1b61881c8405829c50e9cc5c875bfdbf685edf57a76817dfb50643e4a1a"}, + {file = "aiohttp-3.11.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:25de43bb3cf83ad83efc8295af7310219af6dbe4c543c2e74988d8e9c8a2a917"}, + {file = "aiohttp-3.11.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fe7065e2215e4bba63dc00db9ae654c1ba3950a5fff691475a32f511142fcddb"}, + {file = "aiohttp-3.11.13-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7836587eef675a17d835ec3d98a8c9acdbeb2c1d72b0556f0edf4e855a25e9c1"}, + {file = "aiohttp-3.11.13-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:85fa0b18558eb1427090912bd456a01f71edab0872f4e0f9e4285571941e4090"}, + {file = "aiohttp-3.11.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a86dc177eb4c286c19d1823ac296299f59ed8106c9536d2b559f65836e0fb2c6"}, + {file = "aiohttp-3.11.13-cp39-cp39-win32.whl", hash = "sha256:684eea71ab6e8ade86b9021bb62af4bf0881f6be4e926b6b5455de74e420783a"}, + {file = "aiohttp-3.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:82c249f2bfa5ecbe4a1a7902c81c0fba52ed9ebd0176ab3047395d02ad96cfcb"}, + {file = "aiohttp-3.11.13.tar.gz", hash = "sha256:8ce789231404ca8fff7f693cdce398abf6d90fd5dae2b1847477196c243b1fbb"}, ] [package.dependencies] @@ -135,7 +135,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] [[package]] name = "aiolimiter" @@ -211,7 +211,7 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] trio = ["trio (>=0.26.1)"] [[package]] @@ -285,12 +285,12 @@ files = [ markers = {main = "extra == \"voyageai\""} [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] [[package]] name = "babel" @@ -305,7 +305,7 @@ files = [ ] [package.extras] -dev = ["backports.zoneinfo", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata"] +dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] [[package]] name = "beautifulsoup4" @@ -313,11 +313,12 @@ version = "4.13.3" description = "Screen-scraping library" optional = false python-versions = ">=3.7.0" -groups = ["docs"] +groups = ["main", "docs"] files = [ {file = "beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16"}, {file = "beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b"}, ] +markers = {main = "python_version >= \"3.10\" and extra == \"ranx\""} [package.dependencies] soupsieve = ">1.2" @@ -398,19 +399,19 @@ css = ["tinycss2 (>=1.1.0,<1.5)"] [[package]] name = "boto3" -version = "1.36.18" +version = "1.36.0" description = "The AWS SDK for Python" optional = true python-versions = ">=3.8" groups = ["main"] markers = "extra == \"bedrock\"" files = [ - {file = "boto3-1.36.18-py3-none-any.whl", hash = "sha256:084ff25af2d7bda3102d6367f5453e2e83f8cde1da73079ea144595b03cb9400"}, - {file = "boto3-1.36.18.tar.gz", hash = "sha256:be8e32c34d7b103a64fafdd277fa1ec136733b4bbfc11dcfa597efa36a820b37"}, + {file = "boto3-1.36.0-py3-none-any.whl", hash = "sha256:d0ca7a58ce25701a52232cc8df9d87854824f1f2964b929305722ebc7959d5a9"}, + {file = "boto3-1.36.0.tar.gz", hash = "sha256:159898f51c2997a12541c0e02d6e5a8fe2993ddb307b9478fd9a339f98b57e00"}, ] [package.dependencies] -botocore = ">=1.36.18,<1.37.0" +botocore = ">=1.36.0,<1.37.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.11.0,<0.12.0" @@ -419,15 +420,15 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.36.18" +version = "1.36.26" description = "Low-level, data-driven core of boto 3." optional = true python-versions = ">=3.8" groups = ["main"] markers = "extra == \"bedrock\"" files = [ - {file = "botocore-1.36.18-py3-none-any.whl", hash = "sha256:7898d109affd9231c555e71fda88308c1da2db8d39e83d33eb8cb40ebf1ba82f"}, - {file = "botocore-1.36.18.tar.gz", hash = "sha256:ddadafe460e91f11677720a2fcc3ea09c4abb914de2b000da7ba46b4c97da3d7"}, + {file = "botocore-1.36.26-py3-none-any.whl", hash = "sha256:4e3f19913887a58502e71ef8d696fe7eaa54de7813ff73390cd5883f837dfa6e"}, + {file = "botocore-1.36.26.tar.gz", hash = "sha256:4a63bcef7ecf6146fd3a61dc4f9b33b7473b49bdaf1770e9aaca6eee0c9eab62"}, ] [package.dependencies] @@ -443,17 +444,89 @@ crt = ["awscrt (==0.23.8)"] [[package]] name = "cachetools" -version = "5.5.1" +version = "5.5.2" description = "Extensible memoizing collections and decorators" optional = true python-versions = ">=3.7" groups = ["main"] markers = "extra == \"vertexai\"" files = [ - {file = "cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb"}, - {file = "cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95"}, + {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, + {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, +] + +[[package]] +name = "cbor" +version = "1.0.0" +description = "RFC 7049 - Concise Binary Object Representation" +optional = true +python-versions = "*" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "cbor-1.0.0.tar.gz", hash = "sha256:13225a262ddf5615cbd9fd55a76a0d53069d18b07d2e9f19c39e6acb8609bbb6"}, ] +[[package]] +name = "cbor2" +version = "5.6.5" +description = "CBOR (de)serializer with extensive tag support" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "cbor2-5.6.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e16c4a87fc999b4926f5c8f6c696b0d251b4745bc40f6c5aee51d69b30b15ca2"}, + {file = "cbor2-5.6.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:87026fc838370d69f23ed8572939bd71cea2b3f6c8f8bb8283f573374b4d7f33"}, + {file = "cbor2-5.6.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a88f029522aec5425fc2f941b3df90da7688b6756bd3f0472ab886d21208acbd"}, + {file = "cbor2-5.6.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9d15b638539b68aa5d5eacc56099b4543a38b2d2c896055dccf7e83d24b7955"}, + {file = "cbor2-5.6.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:47261f54a024839ec649b950013c4de5b5f521afe592a2688eebbe22430df1dc"}, + {file = "cbor2-5.6.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:559dcf0d897260a9e95e7b43556a62253e84550b77147a1ad4d2c389a2a30192"}, + {file = "cbor2-5.6.5-cp310-cp310-win_amd64.whl", hash = "sha256:5b856fda4c50c5bc73ed3664e64211fa4f015970ed7a15a4d6361bd48462feaf"}, + {file = "cbor2-5.6.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:863e0983989d56d5071270790e7ed8ddbda88c9e5288efdb759aba2efee670bc"}, + {file = "cbor2-5.6.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5cff06464b8f4ca6eb9abcba67bda8f8334a058abc01005c8e616728c387ad32"}, + {file = "cbor2-5.6.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4c7dbcdc59ea7f5a745d3e30ee5e6b6ff5ce7ac244aa3de6786391b10027bb3"}, + {file = "cbor2-5.6.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34cf5ab0dc310c3d0196caa6ae062dc09f6c242e2544bea01691fe60c0230596"}, + {file = "cbor2-5.6.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6797b824b26a30794f2b169c0575301ca9b74ae99064e71d16e6ba0c9057de51"}, + {file = "cbor2-5.6.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:73b9647eed1493097db6aad61e03d8f1252080ee041a1755de18000dd2c05f37"}, + {file = "cbor2-5.6.5-cp311-cp311-win_amd64.whl", hash = "sha256:6e14a1bf6269d25e02ef1d4008e0ce8880aa271d7c6b4c329dba48645764f60e"}, + {file = "cbor2-5.6.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e25c2aebc9db99af7190e2261168cdde8ed3d639ca06868e4f477cf3a228a8e9"}, + {file = "cbor2-5.6.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fde21ac1cf29336a31615a2c469a9cb03cf0add3ae480672d4d38cda467d07fc"}, + {file = "cbor2-5.6.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8947c102cac79d049eadbd5e2ffb8189952890df7cbc3ee262bbc2f95b011a9"}, + {file = "cbor2-5.6.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38886c41bebcd7dca57739439455bce759f1e4c551b511f618b8e9c1295b431b"}, + {file = "cbor2-5.6.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae2b49226224e92851c333b91d83292ec62eba53a19c68a79890ce35f1230d70"}, + {file = "cbor2-5.6.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2764804ffb6553283fc4afb10a280715905a4cea4d6dc7c90d3e89c4a93bc8d"}, + {file = "cbor2-5.6.5-cp312-cp312-win_amd64.whl", hash = "sha256:a3ac50485cf67dfaab170a3e7b527630e93cb0a6af8cdaa403054215dff93adf"}, + {file = "cbor2-5.6.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f0d0a9c5aabd48ecb17acf56004a7542a0b8d8212be52f3102b8218284bd881e"}, + {file = "cbor2-5.6.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61ceb77e6aa25c11c814d4fe8ec9e3bac0094a1f5bd8a2a8c95694596ea01e08"}, + {file = "cbor2-5.6.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97a7e409b864fecf68b2ace8978eb5df1738799a333ec3ea2b9597bfcdd6d7d2"}, + {file = "cbor2-5.6.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6d69f38f7d788b04c09ef2b06747536624b452b3c8b371ab78ad43b0296fab"}, + {file = "cbor2-5.6.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f91e6d74fa6917df31f8757fdd0e154203b0dd0609ec53eb957016a2b474896a"}, + {file = "cbor2-5.6.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5ce13a27ef8fddf643fc17a753fe34aa72b251d03c23da6a560c005dc171085b"}, + {file = "cbor2-5.6.5-cp313-cp313-win_amd64.whl", hash = "sha256:54c72a3207bb2d4480c2c39dad12d7971ce0853a99e3f9b8d559ce6eac84f66f"}, + {file = "cbor2-5.6.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4586a4f65546243096e56a3f18f29d60752ee9204722377021b3119a03ed99ff"}, + {file = "cbor2-5.6.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d1a18b3a58dcd9b40ab55c726160d4a6b74868f2a35b71f9e726268b46dc6a2"}, + {file = "cbor2-5.6.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a83b76367d1c3e69facbcb8cdf65ed6948678e72f433137b41d27458aa2a40cb"}, + {file = "cbor2-5.6.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90bfa36944caccec963e6ab7e01e64e31cc6664535dc06e6295ee3937c999cbb"}, + {file = "cbor2-5.6.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:37096663a5a1c46a776aea44906cbe5fa3952f29f50f349179c00525d321c862"}, + {file = "cbor2-5.6.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:93676af02bd9a0b4a62c17c5b20f8e9c37b5019b1a24db70a2ee6cb770423568"}, + {file = "cbor2-5.6.5-cp38-cp38-win_amd64.whl", hash = "sha256:8f747b7a9aaa58881a0c5b4cd4a9b8fb27eca984ed261a769b61de1f6b5bd1e6"}, + {file = "cbor2-5.6.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:94885903105eec66d7efb55f4ce9884fdc5a4d51f3bd75b6fedc68c5c251511b"}, + {file = "cbor2-5.6.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fe11c2eb518c882cfbeed456e7a552e544893c17db66fe5d3230dbeaca6b615c"}, + {file = "cbor2-5.6.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66dd25dd919cddb0b36f97f9ccfa51947882f064729e65e6bef17c28535dc459"}, + {file = "cbor2-5.6.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa61a02995f3a996c03884cf1a0b5733f88cbfd7fa0e34944bf678d4227ee712"}, + {file = "cbor2-5.6.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:824f202b556fc204e2e9a67d6d6d624e150fbd791278ccfee24e68caec578afd"}, + {file = "cbor2-5.6.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7488aec919f8408f9987a3a32760bd385d8628b23a35477917aa3923ff6ad45f"}, + {file = "cbor2-5.6.5-cp39-cp39-win_amd64.whl", hash = "sha256:a34ee99e86b17444ecbe96d54d909dd1a20e2da9f814ae91b8b71cf1ee2a95e4"}, + {file = "cbor2-5.6.5-py3-none-any.whl", hash = "sha256:3038523b8fc7de312bb9cdcbbbd599987e64307c4db357cd2030c472a6c7d468"}, + {file = "cbor2-5.6.5.tar.gz", hash = "sha256:b682820677ee1dbba45f7da11898d2720f92e06be36acec290867d5ebf3d7e09"}, +] + +[package.extras] +benchmarks = ["pytest-benchmark (==4.0.0)"] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.3.0)", "typing-extensions ; python_version < \"3.12\""] +test = ["coverage (>=7)", "hypothesis", "pytest"] + [[package]] name = "certifi" version = "2025.1.31" @@ -465,7 +538,7 @@ files = [ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, ] -markers = {main = "extra == \"openai\" or extra == \"cohere\" or extra == \"mistralai\" or extra == \"sentence-transformers\" or extra == \"vertexai\" or extra == \"voyageai\""} +markers = {main = "(extra == \"openai\" or extra == \"cohere\" or extra == \"mistralai\" or extra == \"sentence-transformers\" or extra == \"vertexai\" or extra == \"voyageai\" or extra == \"ranx\") and (extra == \"openai\" or extra == \"cohere\" or extra == \"mistralai\" or extra == \"sentence-transformers\" or extra == \"vertexai\" or extra == \"voyageai\") or (extra == \"openai\" or extra == \"cohere\" or extra == \"mistralai\" or extra == \"sentence-transformers\" or extra == \"vertexai\" or extra == \"voyageai\" or extra == \"ranx\") and python_version >= \"3.10\""} [[package]] name = "cffi" @@ -661,7 +734,7 @@ files = [ {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, ] -markers = {main = "extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"voyageai\""} +markers = {main = "(extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"voyageai\" or extra == \"ranx\") and (extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"voyageai\") or (extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"voyageai\" or extra == \"ranx\") and python_version >= \"3.10\""} [[package]] name = "click" @@ -669,11 +742,12 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" -groups = ["dev", "docs"] +groups = ["main", "dev", "docs"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, ] +markers = {main = "extra == \"nltk\""} [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -713,7 +787,7 @@ files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "(extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"openai\") and platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\"", docs = "platform_system == \"Windows\" or sys_platform == \"win32\""} +markers = {main = "platform_system == \"Windows\" and (extra == \"nltk\" or extra == \"openai\" or extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"ranx\") and (extra == \"nltk\" or extra == \"openai\" or extra == \"sentence-transformers\" or extra == \"cohere\" or python_version >= \"3.10\")", dev = "platform_system == \"Windows\" or sys_platform == \"win32\"", docs = "platform_system == \"Windows\" or sys_platform == \"win32\""} [[package]] name = "coloredlogs" @@ -751,6 +825,81 @@ traitlets = ">=4" [package.extras] test = ["pytest"] +[[package]] +name = "contourpy" +version = "1.3.1" +description = "Python library for calculating contours of 2D quadrilateral grids" +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "contourpy-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab"}, + {file = "contourpy-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3"}, + {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277"}, + {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595"}, + {file = "contourpy-1.3.1-cp310-cp310-win32.whl", hash = "sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697"}, + {file = "contourpy-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f"}, + {file = "contourpy-1.3.1-cp311-cp311-win32.whl", hash = "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375"}, + {file = "contourpy-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d"}, + {file = "contourpy-1.3.1-cp312-cp312-win32.whl", hash = "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e"}, + {file = "contourpy-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1"}, + {file = "contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82"}, + {file = "contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53"}, + {file = "contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699"}, +] + +[package.dependencies] +numpy = ">=1.23" + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.11.1)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"] + [[package]] name = "coverage" version = "7.6.12" @@ -825,7 +974,111 @@ files = [ ] [package.extras] -toml = ["tomli"] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] + +[[package]] +name = "cramjam" +version = "2.9.1" +description = "Thin Python bindings to de/compression algorithms in Rust" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "cramjam-2.9.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:8e82464d1e00fbbb12958999b8471ba5e9f3d9711954505a0a7b378762332e6f"}, + {file = "cramjam-2.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d2df8a6511cc08ef1fccd2e0c65e2ebc9f57574ec8376052a76851af5398810"}, + {file = "cramjam-2.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:21ea784e6c3f1843d3523ae0f03651dd06058b39eeb64beb82ee3b100fa83662"}, + {file = "cramjam-2.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e0c5d98a4e791f0bbd0ffcb7dae879baeb2dcc357348a8dc2be0a8c10403a2a"}, + {file = "cramjam-2.9.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e076fd87089197cb61117c63dbe7712ad5eccb93968860eb3bae09b767bac813"}, + {file = "cramjam-2.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d86b44933aea0151e4a2e1e6935448499849045c38167d288ca4c59d5b8cd4e"}, + {file = "cramjam-2.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7eb032549dec897b942ddcf80c1cdccbcb40629f15fc902731dbe6362da49326"}, + {file = "cramjam-2.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf29b4def86ec503e329fe138842a9b79a997e3beb6c7809b05665a0d291edff"}, + {file = "cramjam-2.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a36adf7d13b7accfa206e1c917f08924eb905b45aa8e62176509afa7b14db71e"}, + {file = "cramjam-2.9.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:cf4ea758d98b6fad1b4b2d808d0de690d3162ac56c26968aea0af6524e3eb736"}, + {file = "cramjam-2.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4826d6d81ea490fa7a3ae7a4b9729866a945ffac1f77fe57b71e49d6e1b21efd"}, + {file = "cramjam-2.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:335103317475bf992953c58838152a4761fc3c87354000edbfc4d7e57cf05909"}, + {file = "cramjam-2.9.1-cp310-cp310-win32.whl", hash = "sha256:258120cb1e3afc3443f756f9de161ed63eed56a2c31f6093e81c571c0f2dc9f6"}, + {file = "cramjam-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:c60e5996aa02547d12bc2740d44e90e006b0f93100f53206f7abe6732ad56e69"}, + {file = "cramjam-2.9.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9db1debe48060e41a5b91af9193c524e473c57f6105462c5524a41f5aabdb88"}, + {file = "cramjam-2.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f6f18f0242212d3409d26ce3874937b5b979cebd61f08b633a6ea893c32fc7b6"}, + {file = "cramjam-2.9.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b5b1cd7d39242b2b903cf09cd4696b3a6e04dc537ffa9f3ac8668edae76eecb6"}, + {file = "cramjam-2.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47de0a68f5f4d9951250ef5af31f2a7228132caa9ed60994234f7eb98090d33"}, + {file = "cramjam-2.9.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e13c9a697881e5e38148958612dc6856967f5ff8cd7bba5ff751f2d6ac020aa4"}, + {file = "cramjam-2.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba560244bc1335b420b74e91e35f9d4e7f307a3be3a4603ce0f0d7e15a0acdf0"}, + {file = "cramjam-2.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d47fd41ce260cf4f0ff0e788de961fab9e9c6844a05ce55d06ce31e06107bdc"}, + {file = "cramjam-2.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84d154fbadece82935396eb6bcb502085d944d2fd13b07a94348364344370c2c"}, + {file = "cramjam-2.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:038df668ffb94d64d67b6ecc59cbd206745a425ffc0402897dde12d89fa6a870"}, + {file = "cramjam-2.9.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:4125d8cd86fa08495d310e80926c2f0563f157b76862e7479f9b2cf94823ea0c"}, + {file = "cramjam-2.9.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4206ebdd1d1ef0f3f86c8c2f7c426aa4af6094f4f41e274601fd4c4569f37454"}, + {file = "cramjam-2.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab687bef5c493732b9a4ab870542ee43f5eae0025f9c684c7cb399c3a85cb380"}, + {file = "cramjam-2.9.1-cp311-cp311-win32.whl", hash = "sha256:dda7698b6d7caeae1047adafebc4b43b2a82478234f6c2b45bc3edad854e0600"}, + {file = "cramjam-2.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:872b00ff83e84bcbdc7e951af291ebe65eed20b09c47e7c4af21c312f90b796f"}, + {file = "cramjam-2.9.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:79417957972553502b217a0093532e48893c8b4ca30ccc941cefe9c72379df7c"}, + {file = "cramjam-2.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce2b94117f373defc876f88e74e44049a9969223dbca3240415b71752d0422fb"}, + {file = "cramjam-2.9.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:67040e0fd84404885ec716a806bee6110f9960c3647e0ef1670aab3b7375a70a"}, + {file = "cramjam-2.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bedb84e068b53c944bd08dcb501fd00d67daa8a917922356dd559b484ce7eab"}, + {file = "cramjam-2.9.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:06e3f97a379386d97debf08638a78b3d3850fdf6124755eb270b54905a169930"}, + {file = "cramjam-2.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11118675e9c7952ececabc62f023290ee4f8ecf0bee0d2c7eb8d1c402ee9769d"}, + {file = "cramjam-2.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b7de6b61b11545570e4d6033713f3599525efc615ee353a822be8f6b0c65b77"}, + {file = "cramjam-2.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57ca8f3775324a9de3ee6f05ca172687ba258c0dea79f7e3a6b4112834982f2a"}, + {file = "cramjam-2.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9847dd6f288f1c56359f52acb48ff2df848ff3e3bff34d23855bbcf7016427cc"}, + {file = "cramjam-2.9.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d1248dfa7f151e893ce819670f00879e4b7650b8d4c01279ce4f12140d68dd2"}, + {file = "cramjam-2.9.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9da6d970281083bae91b914362de325414aa03c01fc806f6bb2cc006322ec834"}, + {file = "cramjam-2.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1c33bc095db5733c841a102b8693062be5db8cdac17b9782ebc00577c6a94480"}, + {file = "cramjam-2.9.1-cp312-cp312-win32.whl", hash = "sha256:9e9193cd4bb57e7acd3af24891526299244bfed88168945efdaa09af4e50720f"}, + {file = "cramjam-2.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:15955dd75e80f66c1ea271167a5347661d9bdc365f894a57698c383c9b7d465c"}, + {file = "cramjam-2.9.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5a7797a2fff994fc5e323f7a967a35a3e37e3006ed21d64dcded086502f482af"}, + {file = "cramjam-2.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d51b9b140b1df39a44bff7896d98a10da345b7d5f5ce92368d328c1c2c829167"}, + {file = "cramjam-2.9.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:07ac76b7f992556e7aa910244be11ece578cdf84f4d5d5297461f9a895e18312"}, + {file = "cramjam-2.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d90a72608c7550cd7eba914668f6277bfb0b24f074d1f1bd9d061fcb6f2adbd6"}, + {file = "cramjam-2.9.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56495975401b1821dbe1f29cf222e23556232209a2fdb809fe8156d120ca9c7f"}, + {file = "cramjam-2.9.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b695259e71fde6d5be66b77a4474523ced9ffe9fe8a34cb9b520ec1241a14d3"}, + {file = "cramjam-2.9.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab1e69dc4831bbb79b6d547077aae89074c83e8ad94eba1a3d80e94d2424fd02"}, + {file = "cramjam-2.9.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440b489902bfb7a26d3fec1ca888007615336ff763d2a32a2fc40586548a0dbf"}, + {file = "cramjam-2.9.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:217fe22b41f8c3dce03852f828b059abfad11d1344a1df2f43d3eb8634b18d75"}, + {file = "cramjam-2.9.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:95f3646ddc98af25af25d5692ae65966488a283813336ea9cf41b22e542e7c0d"}, + {file = "cramjam-2.9.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:6b19fc60ead1cae9795a5b359599da3a1c95d38f869bdfb51c441fd76b04e926"}, + {file = "cramjam-2.9.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8dc5207567459d049696f62a1fdfb220f3fe6aa0d722285d44753e12504dac6c"}, + {file = "cramjam-2.9.1-cp313-cp313-win32.whl", hash = "sha256:fbfe35929a61b914de9e5dbacde0cfbba86cbf5122f9285a24c14ed0b645490b"}, + {file = "cramjam-2.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:06068bd191a82ad4fc1ac23d6f8627fb5e37ec4be0431711b9a2dbacaccfeddb"}, + {file = "cramjam-2.9.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:6a2ca4d3c683d28d3217821029eb08d3487d5043d7eb455df11ff3cacfd4c916"}, + {file = "cramjam-2.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:008b49b455b396acc5459dfb06fb9d56049c4097ee8e590892a4d3da9a711da3"}, + {file = "cramjam-2.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:45c18cc13156e8697a8d3f9e57e49a69b00e14a103196efab0893fae1a5257f8"}, + {file = "cramjam-2.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d14a0efb21e0fec0631bcd66040b06e6a0fe10825f3aacffded38c1c978bdff9"}, + {file = "cramjam-2.9.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3f815fb0eba625af45139af4f90f5fc2ddda61b171c2cc3ab63d44b40c5c7768"}, + {file = "cramjam-2.9.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04828cbfad7384f06a4a7d0d927c3e85ef11dc5a40b9cf5f3e29ac4e23ecd678"}, + {file = "cramjam-2.9.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0944a7c3a78f940c06d1b29bdce91a17798d80593dd01ebfeb842761e48a8b5"}, + {file = "cramjam-2.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec769e5b16251704502277a1163dcf2611551452d7590ff4cc422b7b0367fc96"}, + {file = "cramjam-2.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ba79c7d2cc5adb897b690c05dd9b67c4d401736d207314b99315f7be3cd94fd"}, + {file = "cramjam-2.9.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d35923fb5411bde30b53c0696dff8e24c8a38b010b89544834c53f4462fd71df"}, + {file = "cramjam-2.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:da0cc0efdbfb8ee2361f89f38ded03d11678f37e392afff7a97b09c55dadfc83"}, + {file = "cramjam-2.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f89924858712b8b936f04f3d690e72825a3e5127a140b434c79030c1c5a887ce"}, + {file = "cramjam-2.9.1-cp38-cp38-win32.whl", hash = "sha256:5925a738b8478f223ab9756fc794e3cabd5917fd7846f66adcf1d5fc2bf9864c"}, + {file = "cramjam-2.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:b7ac273498a2c6772d67707e101b74014c0d9413bb4711c51d8ec311de59b4b1"}, + {file = "cramjam-2.9.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:af39006faddfc6253beb93ca821d544931cfee7f0177b99ff106dfd8fd6a2cd8"}, + {file = "cramjam-2.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b3291be0d3f73d5774d69013be4ab33978c777363b5312d14f62f77817c2f75a"}, + {file = "cramjam-2.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1539fd758f0e57fad7913cebff8baaee871bb561ddf6fa710a427b74da6b6778"}, + {file = "cramjam-2.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff362f68bd68ac0eccb445209238d589bba728fb6d7f2e9dc199e0ec3a61d6e0"}, + {file = "cramjam-2.9.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23b9786d1d17686fb8d600ade2a19374c7188d4b8867efa9af0d8274a220aec7"}, + {file = "cramjam-2.9.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bc9c2c748aaf91863d89c4583f529c1c709485c94f8dfeb3ee48662d88e3258"}, + {file = "cramjam-2.9.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd0fa9a0e7f18224b6d2d1d69dbdc3aecec80ef1393c59244159b131604a4395"}, + {file = "cramjam-2.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ceef6e09ee22457997370882aa3c69de01e6dd0aaa2f953e1e87ad11641d042"}, + {file = "cramjam-2.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1376f6fdbf0b30712413a0b4e51663a4938ae2f6b449f8e4635dbb3694db83cf"}, + {file = "cramjam-2.9.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:342fb946f8d3e9e35b837288b03ab23cfbe0bb5a30e582ed805ef79706823a96"}, + {file = "cramjam-2.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a237064a6e2c2256c9a1cf2beb7c971382190c0f1eb2e810e02e971881756132"}, + {file = "cramjam-2.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53145fc9f2319c1245d4329e1da8cfacd6e35e27090c07c0b9d453ae2bbdac3e"}, + {file = "cramjam-2.9.1-cp39-cp39-win32.whl", hash = "sha256:8a9f52c27292c21457f43c4ce124939302a9acfb62295e7cda8667310563a5a3"}, + {file = "cramjam-2.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:8097ee39b61c86848a443c0b25b2df1de6b331fd512b20836a4f5cfde51ab255"}, + {file = "cramjam-2.9.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:86824c695688fcd06c5ac9bbd3fea9bdfb4cca194b1e706fbf11a629df48d2b4"}, + {file = "cramjam-2.9.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:27571bfa5a5d618604696747d0dc1d2a99b5906c967c8dee53c13a7107edfde6"}, + {file = "cramjam-2.9.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb01f6e38719818778144d3165a89ea1ad9dc58c6342b7f20aa194c70f34cbd1"}, + {file = "cramjam-2.9.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b5cef5cf40725fe64592af9ec163e7389855077700678a1d94bec549403a74d"}, + {file = "cramjam-2.9.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ac48b978aa0675f62b642750e798c394a64d25ce852e4e541f69bef9a564c2f0"}, + {file = "cramjam-2.9.1.tar.gz", hash = "sha256:336cc591d86cbd225d256813779f46624f857bc9c779db126271eff9ddc524ae"}, +] + +[package.extras] +dev = ["black (==22.3.0)", "hypothesis", "numpy", "pytest (>=5.30)", "pytest-benchmark", "pytest-xdist"] [[package]] name = "cryptography" @@ -872,15 +1125,32 @@ files = [ cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0) ; python_version >= \"3.8\""] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] -pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_version >= \"3.8\""] +pep8test = ["check-sdist ; python_version >= \"3.8\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==44.0.1)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "cycler" +version = "0.12.1" +description = "Composable style cycles" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, + {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, +] + +[package.extras] +docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] +tests = ["pytest", "pytest-cov", "pytest-xdist"] + [[package]] name = "debugpy" version = "1.8.12" @@ -919,14 +1189,14 @@ files = [ [[package]] name = "decorator" -version = "5.1.1" +version = "5.2.1" description = "Decorators for Humans" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" groups = ["dev", "docs"] files = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, + {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, + {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, ] [[package]] @@ -1057,7 +1327,7 @@ files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] -markers = {main = "(extra == \"openai\" or extra == \"cohere\" or extra == \"mistralai\") and python_version < \"3.11\"", dev = "python_version < \"3.11\"", docs = "python_version < \"3.11\""} +markers = {main = "(extra == \"openai\" or extra == \"cohere\" or extra == \"mistralai\") and python_version <= \"3.10\"", dev = "python_version <= \"3.10\"", docs = "python_version <= \"3.10\""} [package.extras] test = ["pytest (>=6)"] @@ -1090,7 +1360,7 @@ files = [ ] [package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""] [[package]] name = "fastavro" @@ -1155,6 +1425,68 @@ files = [ [package.extras] devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] +[[package]] +name = "fastparquet" +version = "2024.11.0" +description = "Python support for Parquet file format" +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "fastparquet-2024.11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:60ccf587410f0979105e17036df61bb60e1c2b81880dc91895cdb4ee65b71e7f"}, + {file = "fastparquet-2024.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a5ad5fc14b0567e700bea3cd528a0bd45a6f9371370b49de8889fb3d10a6574a"}, + {file = "fastparquet-2024.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b74333914f454344458dab9d1432fda9b70d62e28dc7acb1512d937ef1424ee"}, + {file = "fastparquet-2024.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41d1610130b5cb1ce36467766191c5418cba8631e2bfe3affffaf13f9be4e7a8"}, + {file = "fastparquet-2024.11.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d281edd625c33628ba028d3221180283d6161bc5ceb55eae1f0ca1678f864f26"}, + {file = "fastparquet-2024.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fa56b19a29008c34cfe8831e810f770080debcbffc69aabd1df4d47572181f9c"}, + {file = "fastparquet-2024.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5914ecfa766b7763201b9f49d832a5e89c2dccad470ca4f9c9b228d9a8349756"}, + {file = "fastparquet-2024.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:561202e8f0e859ccc1aa77c4aaad1d7901b2d50fd6f624ca018bae4c3c7a62ce"}, + {file = "fastparquet-2024.11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:374cdfa745aa7d5188430528d5841cf823eb9ad16df72ad6dadd898ccccce3be"}, + {file = "fastparquet-2024.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c8401bfd86cccaf0ab7c0ade58c91ae19317ff6092e1d4ad96c2178197d8124"}, + {file = "fastparquet-2024.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9cca4c6b5969df5561c13786f9d116300db1ec22c7941e237cfca4ce602f59b"}, + {file = "fastparquet-2024.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a9387e77ac608d8978774caaf1e19de67eaa1386806e514dcb19f741b19cfe5"}, + {file = "fastparquet-2024.11.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6595d3771b3d587a31137e985f751b4d599d5c8e9af9c4858e373fdf5c3f8720"}, + {file = "fastparquet-2024.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:053695c2f730b78a2d3925df7cd5c6444d6c1560076af907993361cc7accf3e2"}, + {file = "fastparquet-2024.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a52eecc6270ae15f0d51347c3f762703dd667ca486f127dc0a21e7e59856ae5"}, + {file = "fastparquet-2024.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:e29ff7a367fafa57c6896fb6abc84126e2466811aefd3e4ad4070b9e18820e54"}, + {file = "fastparquet-2024.11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dbad4b014782bd38b58b8e9f514fe958cfa7a6c4e187859232d29fd5c5ddd849"}, + {file = "fastparquet-2024.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:403d31109d398b6be7ce84fa3483fc277c6a23f0b321348c0a505eb098a041cb"}, + {file = "fastparquet-2024.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbbb9057a26acf0abad7adf58781ee357258b7708ee44a289e3bee97e2f55d42"}, + {file = "fastparquet-2024.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63e0e416e25c15daa174aad8ba991c2e9e5b0dc347e5aed5562124261400f87b"}, + {file = "fastparquet-2024.11.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e2d7f02f57231e6c86d26e9ea71953737202f20e948790e5d4db6d6a1a150dc"}, + {file = "fastparquet-2024.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fbe4468146b633d8f09d7b196fea0547f213cb5ce5f76e9d1beb29eaa9593a93"}, + {file = "fastparquet-2024.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:29d5c718817bcd765fc519b17f759cad4945974421ecc1931d3bdc3e05e57fa9"}, + {file = "fastparquet-2024.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:74a0b3c40ab373442c0fda96b75a36e88745d8b138fcc3a6143e04682cbbb8ca"}, + {file = "fastparquet-2024.11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:59e5c5b51083d5b82572cdb7aed0346e3181e3ac9d2e45759da2e804bdafa7ee"}, + {file = "fastparquet-2024.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdadf7b6bad789125b823bfc5b0a719ba5c4a2ef965f973702d3ea89cff057f6"}, + {file = "fastparquet-2024.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46b2db02fc2a1507939d35441c8ab211d53afd75d82eec9767d1c3656402859b"}, + {file = "fastparquet-2024.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3afdef2895c9f459135a00a7ed3ceafebfbce918a9e7b5d550e4fae39c1b64d"}, + {file = "fastparquet-2024.11.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36b5c9bd2ffaaa26ff45d59a6cefe58503dd748e0c7fad80dd905749da0f2b9e"}, + {file = "fastparquet-2024.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6b7df5d3b61a19d76e209fe8d3133759af1c139e04ebc6d43f3cc2d8045ef338"}, + {file = "fastparquet-2024.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b35823ac7a194134e5f82fa4a9659e42e8f9ad1f2d22a55fbb7b9e4053aabbb"}, + {file = "fastparquet-2024.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:d20632964e65530374ff7cddd42cc06aa0a1388934903693d6d22592a5ba827b"}, + {file = "fastparquet-2024.11.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1ae953c0e3832ae3936b6d92fde493ac7d8b775d7d59d02f7f46f67e1c21ed24"}, + {file = "fastparquet-2024.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:869e167a4067116b4a27eb7adbe597130b2e2e9cfc0f3e84f60e2e182a933f23"}, + {file = "fastparquet-2024.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb3356862fba2f9b2ea8e679d66901f466c92be8e023439fe854bc392fbf40a6"}, + {file = "fastparquet-2024.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc475993232c6a64f350aeb928013a807eb93f78675810fd019cbcff39f6baf3"}, + {file = "fastparquet-2024.11.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d24c923a2d9d22a5e7564245f856e6462d524d57982ac8f7479cde991ff73362"}, + {file = "fastparquet-2024.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6b936dcf40ca5fff9e70383d48811b1482b871ff74af857cb4db5f4d072f01ab"}, + {file = "fastparquet-2024.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4abd3426607335e5ad09be29ef4eeccdf097710e44420deac16893cee64ea0d8"}, + {file = "fastparquet-2024.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:6ec7b398a86432993441d0a08dfae59e29649c803ed64ec4b1d7c3e0855b14cb"}, + {file = "fastparquet-2024.11.0.tar.gz", hash = "sha256:e3b1fc73fd3e1b70b0de254bae7feb890436cb67e99458b88cb9bd3cc44db419"}, +] + +[package.dependencies] +cramjam = ">=2.3" +fsspec = "*" +numpy = "*" +packaging = "*" +pandas = ">=1.5.0" + +[package.extras] +lzo = ["python-lzo"] + [[package]] name = "filelock" version = "3.17.0" @@ -1171,7 +1503,82 @@ markers = {main = "extra == \"sentence-transformers\" or extra == \"cohere\""} [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] -typing = ["typing-extensions (>=4.12.2)"] +typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] + +[[package]] +name = "fonttools" +version = "4.56.0" +description = "Tools to manipulate font files" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "fonttools-4.56.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:331954d002dbf5e704c7f3756028e21db07097c19722569983ba4d74df014000"}, + {file = "fonttools-4.56.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8d1613abd5af2f93c05867b3a3759a56e8bf97eb79b1da76b2bc10892f96ff16"}, + {file = "fonttools-4.56.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:705837eae384fe21cee5e5746fd4f4b2f06f87544fa60f60740007e0aa600311"}, + {file = "fonttools-4.56.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc871904a53a9d4d908673c6faa15689874af1c7c5ac403a8e12d967ebd0c0dc"}, + {file = "fonttools-4.56.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:38b947de71748bab150259ee05a775e8a0635891568e9fdb3cdd7d0e0004e62f"}, + {file = "fonttools-4.56.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:86b2a1013ef7a64d2e94606632683f07712045ed86d937c11ef4dde97319c086"}, + {file = "fonttools-4.56.0-cp310-cp310-win32.whl", hash = "sha256:133bedb9a5c6376ad43e6518b7e2cd2f866a05b1998f14842631d5feb36b5786"}, + {file = "fonttools-4.56.0-cp310-cp310-win_amd64.whl", hash = "sha256:17f39313b649037f6c800209984a11fc256a6137cbe5487091c6c7187cae4685"}, + {file = "fonttools-4.56.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ef04bc7827adb7532be3d14462390dd71287644516af3f1e67f1e6ff9c6d6df"}, + {file = "fonttools-4.56.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ffda9b8cd9cb8b301cae2602ec62375b59e2e2108a117746f12215145e3f786c"}, + {file = "fonttools-4.56.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e993e8db36306cc3f1734edc8ea67906c55f98683d6fd34c3fc5593fdbba4c"}, + {file = "fonttools-4.56.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:003548eadd674175510773f73fb2060bb46adb77c94854af3e0cc5bc70260049"}, + {file = "fonttools-4.56.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd9825822e7bb243f285013e653f6741954d8147427aaa0324a862cdbf4cbf62"}, + {file = "fonttools-4.56.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b23d30a2c0b992fb1c4f8ac9bfde44b5586d23457759b6cf9a787f1a35179ee0"}, + {file = "fonttools-4.56.0-cp311-cp311-win32.whl", hash = "sha256:47b5e4680002ae1756d3ae3b6114e20aaee6cc5c69d1e5911f5ffffd3ee46c6b"}, + {file = "fonttools-4.56.0-cp311-cp311-win_amd64.whl", hash = "sha256:14a3e3e6b211660db54ca1ef7006401e4a694e53ffd4553ab9bc87ead01d0f05"}, + {file = "fonttools-4.56.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6f195c14c01bd057bc9b4f70756b510e009c83c5ea67b25ced3e2c38e6ee6e9"}, + {file = "fonttools-4.56.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa760e5fe8b50cbc2d71884a1eff2ed2b95a005f02dda2fa431560db0ddd927f"}, + {file = "fonttools-4.56.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d54a45d30251f1d729e69e5b675f9a08b7da413391a1227781e2a297fa37f6d2"}, + {file = "fonttools-4.56.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661a8995d11e6e4914a44ca7d52d1286e2d9b154f685a4d1f69add8418961563"}, + {file = "fonttools-4.56.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d94449ad0a5f2a8bf5d2f8d71d65088aee48adbe45f3c5f8e00e3ad861ed81a"}, + {file = "fonttools-4.56.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f59746f7953f69cc3290ce2f971ab01056e55ddd0fb8b792c31a8acd7fee2d28"}, + {file = "fonttools-4.56.0-cp312-cp312-win32.whl", hash = "sha256:bce60f9a977c9d3d51de475af3f3581d9b36952e1f8fc19a1f2254f1dda7ce9c"}, + {file = "fonttools-4.56.0-cp312-cp312-win_amd64.whl", hash = "sha256:300c310bb725b2bdb4f5fc7e148e190bd69f01925c7ab437b9c0ca3e1c7cd9ba"}, + {file = "fonttools-4.56.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f20e2c0dfab82983a90f3d00703ac0960412036153e5023eed2b4641d7d5e692"}, + {file = "fonttools-4.56.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f36a0868f47b7566237640c026c65a86d09a3d9ca5df1cd039e30a1da73098a0"}, + {file = "fonttools-4.56.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62b4c6802fa28e14dba010e75190e0e6228513573f1eeae57b11aa1a39b7e5b1"}, + {file = "fonttools-4.56.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a05d1f07eb0a7d755fbe01fee1fd255c3a4d3730130cf1bfefb682d18fd2fcea"}, + {file = "fonttools-4.56.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0073b62c3438cf0058488c002ea90489e8801d3a7af5ce5f7c05c105bee815c3"}, + {file = "fonttools-4.56.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cad98c94833465bcf28f51c248aaf07ca022efc6a3eba750ad9c1e0256d278"}, + {file = "fonttools-4.56.0-cp313-cp313-win32.whl", hash = "sha256:d0cb73ccf7f6d7ca8d0bc7ea8ac0a5b84969a41c56ac3ac3422a24df2680546f"}, + {file = "fonttools-4.56.0-cp313-cp313-win_amd64.whl", hash = "sha256:62cc1253827d1e500fde9dbe981219fea4eb000fd63402283472d38e7d8aa1c6"}, + {file = "fonttools-4.56.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3fd3fccb7b9adaaecfa79ad51b759f2123e1aba97f857936ce044d4f029abd71"}, + {file = "fonttools-4.56.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:193b86e9f769320bc98ffdb42accafb5d0c8c49bd62884f1c0702bc598b3f0a2"}, + {file = "fonttools-4.56.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e81c1cc80c1d8bf071356cc3e0e25071fbba1c75afc48d41b26048980b3c771"}, + {file = "fonttools-4.56.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9270505a19361e81eecdbc2c251ad1e1a9a9c2ad75fa022ccdee533f55535dc"}, + {file = "fonttools-4.56.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:53f5e9767978a4daf46f28e09dbeb7d010319924ae622f7b56174b777258e5ba"}, + {file = "fonttools-4.56.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9da650cb29bc098b8cfd15ef09009c914b35c7986c8fa9f08b51108b7bc393b4"}, + {file = "fonttools-4.56.0-cp38-cp38-win32.whl", hash = "sha256:965d0209e6dbdb9416100123b6709cb13f5232e2d52d17ed37f9df0cc31e2b35"}, + {file = "fonttools-4.56.0-cp38-cp38-win_amd64.whl", hash = "sha256:654ac4583e2d7c62aebc6fc6a4c6736f078f50300e18aa105d87ce8925cfac31"}, + {file = "fonttools-4.56.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca7962e8e5fc047cc4e59389959843aafbf7445b6c08c20d883e60ced46370a5"}, + {file = "fonttools-4.56.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1af375734018951c31c0737d04a9d5fd0a353a0253db5fbed2ccd44eac62d8c"}, + {file = "fonttools-4.56.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:442ad4122468d0e47d83bc59d0e91b474593a8c813839e1872e47c7a0cb53b10"}, + {file = "fonttools-4.56.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cf4f8d2a30b454ac682e12c61831dcb174950c406011418e739de592bbf8f76"}, + {file = "fonttools-4.56.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:96a4271f63a615bcb902b9f56de00ea225d6896052c49f20d0c91e9f43529a29"}, + {file = "fonttools-4.56.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6c1d38642ca2dddc7ae992ef5d026e5061a84f10ff2b906be5680ab089f55bb8"}, + {file = "fonttools-4.56.0-cp39-cp39-win32.whl", hash = "sha256:2d351275f73ebdd81dd5b09a8b8dac7a30f29a279d41e1c1192aedf1b6dced40"}, + {file = "fonttools-4.56.0-cp39-cp39-win_amd64.whl", hash = "sha256:d6ca96d1b61a707ba01a43318c9c40aaf11a5a568d1e61146fafa6ab20890793"}, + {file = "fonttools-4.56.0-py3-none-any.whl", hash = "sha256:1088182f68c303b50ca4dc0c82d42083d176cba37af1937e1a976a31149d4d14"}, + {file = "fonttools-4.56.0.tar.gz", hash = "sha256:a114d1567e1a1586b7e9e7fc2ff686ca542a82769a296cef131e4c4af51e58f4"}, +] + +[package.extras] +all = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\"", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0) ; python_version <= \"3.12\"", "xattr ; sys_platform == \"darwin\"", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\""] +lxml = ["lxml (>=4.0)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr ; sys_platform == \"darwin\""] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.1.0) ; python_version <= \"3.12\""] +woff = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "zopfli (>=0.1.4)"] [[package]] name = "frozenlist" @@ -1283,7 +1690,7 @@ description = "File-system specification" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"sentence-transformers\" or extra == \"cohere\"" +markers = "(extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"ranx\") and (extra == \"sentence-transformers\" or extra == \"cohere\") or (extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"ranx\") and python_version >= \"3.10\"" files = [ {file = "fsspec-2025.2.0-py3-none-any.whl", hash = "sha256:9de2ad9ce1f85e1931858535bc882543171d197001a0a5eb2ddc04f1781ab95b"}, {file = "fsspec-2025.2.0.tar.gz", hash = "sha256:1c24b16eaa0a1798afa0337aa0db9b256718ab2a89c425371f5628d22c3b6afd"}, @@ -1334,15 +1741,15 @@ files = [ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = [ - {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}, {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, ] grpcio-status = [ - {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "extra == \"grpc\""}, {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, ] proto-plus = [ - {version = ">=1.22.3,<2.0.0dev", markers = "python_version < \"3.13\""}, + {version = ">=1.22.3,<2.0.0dev"}, {version = ">=1.25.0,<2.0.0dev", markers = "python_version >= \"3.13\""}, ] protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" @@ -1350,7 +1757,7 @@ requests = ">=2.18.0,<3.0.0.dev0" [package.extras] async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.dev0)"] -grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev) ; python_version >= \"3.11\"", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0) ; python_version >= \"3.11\""] grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] @@ -1382,15 +1789,15 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-aiplatform" -version = "1.79.0" +version = "1.82.0" description = "Vertex AI API client library" optional = true python-versions = ">=3.8" groups = ["main"] markers = "extra == \"vertexai\"" files = [ - {file = "google_cloud_aiplatform-1.79.0-py2.py3-none-any.whl", hash = "sha256:e52d518c386ce2b4ce57f1b73b46c57531d9a6ccd70c21a37b349f428bfc1c3f"}, - {file = "google_cloud_aiplatform-1.79.0.tar.gz", hash = "sha256:362bfd16716dcfb6c131736f25246790002b29c99a246fcf4c08a7c71bd2301f"}, + {file = "google_cloud_aiplatform-1.82.0-py2.py3-none-any.whl", hash = "sha256:13368a961b2bfa8f46ccd10371bb19bd5f946d8f29c411726061ed1a140ce890"}, + {file = "google_cloud_aiplatform-1.82.0.tar.gz", hash = "sha256:b7ea7379249cc1821aa46300a16e4b15aa64aa22665e2536b2bcb7e473d7438e"}, ] [package.dependencies] @@ -1408,39 +1815,42 @@ shapely = "<3.0.0dev" typing-extensions = "*" [package.extras] +ag2 = ["ag2[gemini]"] +ag2-testing = ["absl-py", "ag2[gemini]", "cloudpickle (>=3.0,<4.0)", "google-cloud-trace (<2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)", "pytest-xdist", "typing-extensions"] +agent-engines = ["cloudpickle (>=3.0,<4.0)", "google-cloud-logging (<4)", "google-cloud-trace (<2)", "packaging (>=24.0)", "pydantic (>=2.10,<3)", "typing-extensions"] autologging = ["mlflow (>=1.27.0,<=2.16.0)"] cloud-profiler = ["tensorboard-plugin-profile (>=2.4.0,<2.18.0)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] -datasets = ["pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)"] +datasets = ["pyarrow (>=10.0.1) ; python_version == \"3.11\"", "pyarrow (>=14.0.0) ; python_version >= \"3.12\"", "pyarrow (>=3.0.0,<8.0dev) ; python_version < \"3.11\""] endpoint = ["requests (>=2.28.1)"] -evaluation = ["pandas (>=1.0.0)", "scikit-learn", "scikit-learn (<1.6.0)", "tqdm (>=4.23.0)"] -full = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.114.0)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-vizier (>=0.1.6)", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.16.0)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<2.10.dev0 || >=2.33.dev0,<=2.33.0)", "ray[default] (>=2.5,<=2.33.0)", "requests (>=2.28.1)", "scikit-learn", "scikit-learn (<1.6.0)", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<2.18.0)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "tqdm (>=4.23.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)"] -langchain = ["langchain (>=0.1.16,<0.4)", "langchain-core (<0.4)", "langchain-google-vertexai (<3)", "langgraph (>=0.2.45,<0.3)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)"] -langchain-testing = ["absl-py", "cloudpickle (>=3.0,<4.0)", "google-cloud-trace (<2)", "langchain (>=0.1.16,<0.4)", "langchain-core (<0.4)", "langchain-google-vertexai (<3)", "langgraph (>=0.2.45,<0.3)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)", "pytest-xdist", "typing-extensions"] +evaluation = ["pandas (>=1.0.0)", "scikit-learn (<1.6.0) ; python_version <= \"3.10\"", "scikit-learn ; python_version > \"3.10\"", "tqdm (>=4.23.0)"] +full = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.114.0)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-vizier (>=0.1.6)", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.16.0)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=10.0.1) ; python_version == \"3.11\"", "pyarrow (>=14.0.0) ; python_version >= \"3.12\"", "pyarrow (>=3.0.0,<8.0dev) ; python_version < \"3.11\"", "pyarrow (>=6.0.1)", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<2.10.dev0 || >=2.33.dev0,<=2.33.0) ; python_version < \"3.11\"", "ray[default] (>=2.5,<=2.33.0) ; python_version == \"3.11\"", "requests (>=2.28.1)", "scikit-learn (<1.6.0) ; python_version <= \"3.10\"", "scikit-learn ; python_version > \"3.10\"", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<2.18.0)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev) ; python_version <= \"3.11\"", "tensorflow (>=2.4.0,<3.0.0dev)", "tqdm (>=4.23.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)"] +langchain = ["langchain (>=0.3,<0.4)", "langchain-core (>=0.3,<0.4)", "langchain-google-vertexai (>=2,<3)", "langgraph (>=0.2.45,<0.3)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)"] +langchain-testing = ["absl-py", "cloudpickle (>=3.0,<4.0)", "google-cloud-trace (<2)", "langchain (>=0.3,<0.4)", "langchain-core (>=0.3,<0.4)", "langchain-google-vertexai (>=2,<3)", "langgraph (>=0.2.45,<0.3)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)", "pytest-xdist", "typing-extensions"] lit = ["explainable-ai-sdk (>=1.0.0)", "lit-nlp (==0.4.0)", "pandas (>=1.0.0)", "tensorflow (>=2.3.0,<3.0.0dev)"] metadata = ["numpy (>=1.15.0)", "pandas (>=1.0.0)"] pipelines = ["pyyaml (>=5.3.1,<7)"] prediction = ["docker (>=5.0.3)", "fastapi (>=0.71.0,<=0.114.0)", "httpx (>=0.23.0,<0.25.0)", "starlette (>=0.17.1)", "uvicorn[standard] (>=0.16.0)"] private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"] -ray = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0)", "pyarrow (>=6.0.1)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<2.10.dev0 || >=2.33.dev0,<=2.33.0)", "ray[default] (>=2.5,<=2.33.0)", "setuptools (<70.0.0)"] -ray-testing = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0)", "pyarrow (>=6.0.1)", "pytest-xdist", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<2.10.dev0 || >=2.33.dev0,<=2.33.0)", "ray[default] (>=2.5,<=2.33.0)", "ray[train]", "scikit-learn (<1.6.0)", "setuptools (<70.0.0)", "tensorflow", "torch (>=2.0.0,<2.1.0)", "xgboost", "xgboost-ray"] +ray = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0)", "pyarrow (>=6.0.1)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<2.10.dev0 || >=2.33.dev0,<=2.33.0) ; python_version < \"3.11\"", "ray[default] (>=2.5,<=2.33.0) ; python_version == \"3.11\"", "setuptools (<70.0.0)"] +ray-testing = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0)", "pyarrow (>=6.0.1)", "pytest-xdist", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<2.10.dev0 || >=2.33.dev0,<=2.33.0) ; python_version < \"3.11\"", "ray[default] (>=2.5,<=2.33.0) ; python_version == \"3.11\"", "ray[train]", "scikit-learn (<1.6.0)", "setuptools (<70.0.0)", "tensorflow", "torch (>=2.0.0,<2.1.0)", "xgboost", "xgboost-ray"] reasoningengine = ["cloudpickle (>=3.0,<4.0)", "google-cloud-trace (<2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)", "typing-extensions"] -tensorboard = ["tensorboard-plugin-profile (>=2.4.0,<2.18.0)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] -testing = ["aiohttp", "bigframes", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.114.0)", "google-api-core (>=2.11,<3.0.0)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-vizier (>=0.1.6)", "grpcio-testing", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "ipython", "kfp (>=2.6.0,<3.0.0)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.16.0)", "nltk", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<2.10.dev0 || >=2.33.dev0,<=2.33.0)", "ray[default] (>=2.5,<=2.33.0)", "requests (>=2.28.1)", "requests-toolbelt (<1.0.0)", "scikit-learn", "scikit-learn (<1.6.0)", "sentencepiece (>=0.2.0)", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<2.18.0)", "tensorflow (==2.13.0)", "tensorflow (==2.16.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "torch (>=2.0.0,<2.1.0)", "torch (>=2.2.0)", "tqdm (>=4.23.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] +tensorboard = ["tensorboard-plugin-profile (>=2.4.0,<2.18.0)", "tensorflow (>=2.3.0,<3.0.0dev) ; python_version <= \"3.11\"", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] +testing = ["aiohttp", "bigframes ; python_version >= \"3.10\"", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.114.0)", "google-api-core (>=2.11,<3.0.0)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-vizier (>=0.1.6)", "grpcio-testing", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "ipython", "kfp (>=2.6.0,<3.0.0)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.16.0)", "nltk", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=10.0.1) ; python_version == \"3.11\"", "pyarrow (>=14.0.0) ; python_version >= \"3.12\"", "pyarrow (>=3.0.0,<8.0dev) ; python_version < \"3.11\"", "pyarrow (>=6.0.1)", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<2.10.dev0 || >=2.33.dev0,<=2.33.0) ; python_version < \"3.11\"", "ray[default] (>=2.5,<=2.33.0) ; python_version == \"3.11\"", "requests (>=2.28.1)", "requests-toolbelt (<1.0.0)", "scikit-learn (<1.6.0) ; python_version <= \"3.10\"", "scikit-learn ; python_version > \"3.10\"", "sentencepiece (>=0.2.0)", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<2.18.0)", "tensorflow (==2.13.0) ; python_version <= \"3.11\"", "tensorflow (==2.16.1) ; python_version > \"3.11\"", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev) ; python_version <= \"3.11\"", "tensorflow (>=2.4.0,<3.0.0dev)", "torch (>=2.0.0,<2.1.0) ; python_version <= \"3.11\"", "torch (>=2.2.0) ; python_version > \"3.11\"", "tqdm (>=4.23.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] tokenization = ["sentencepiece (>=0.2.0)"] vizier = ["google-vizier (>=0.1.6)"] xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] [[package]] name = "google-cloud-bigquery" -version = "3.29.0" +version = "3.30.0" description = "Google BigQuery API client library" optional = true python-versions = ">=3.7" groups = ["main"] markers = "extra == \"vertexai\"" files = [ - {file = "google_cloud_bigquery-3.29.0-py2.py3-none-any.whl", hash = "sha256:5453a4eabe50118254eda9778f3d7dad413490de5f7046b5e66c98f5a1580308"}, - {file = "google_cloud_bigquery-3.29.0.tar.gz", hash = "sha256:fafc2b455ffce3bcc6ce0e884184ef50b6a11350a83b91e327fadda4d5566e72"}, + {file = "google_cloud_bigquery-3.30.0-py2.py3-none-any.whl", hash = "sha256:f4d28d846a727f20569c9b2d2f4fa703242daadcb2ec4240905aa485ba461877"}, + {file = "google_cloud_bigquery-3.30.0.tar.gz", hash = "sha256:7e27fbafc8ed33cc200fe05af12ecd74d279fe3da6692585a3cef7aee90575b6"}, ] [package.dependencies] @@ -1455,25 +1865,25 @@ requests = ">=2.21.0,<3.0.0dev" [package.extras] all = ["google-cloud-bigquery[bigquery-v2,bqstorage,geopandas,ipython,ipywidgets,opentelemetry,pandas,tqdm]"] bigquery-v2 = ["proto-plus (>=1.22.3,<2.0.0dev)", "protobuf (>=3.20.2,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<6.0.0dev)"] -bqstorage = ["google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "pyarrow (>=3.0.0)"] +bqstorage = ["google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev) ; python_version >= \"3.11\"", "pyarrow (>=3.0.0)"] geopandas = ["Shapely (>=1.8.4,<3.0.0dev)", "geopandas (>=0.9.0,<2.0dev)"] ipython = ["bigquery-magics (>=0.1.0)"] ipywidgets = ["ipykernel (>=6.0.0)", "ipywidgets (>=7.7.0)"] opentelemetry = ["opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)"] -pandas = ["db-dtypes (>=0.3.0,<2.0.0dev)", "importlib-metadata (>=1.0.0)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)"] +pandas = ["db-dtypes (>=0.3.0,<2.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev) ; python_version >= \"3.11\"", "importlib-metadata (>=1.0.0) ; python_version < \"3.8\"", "pandas (>=1.1.0)", "pandas-gbq (>=0.26.1) ; python_version >= \"3.8\"", "pyarrow (>=3.0.0)"] tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] [[package]] name = "google-cloud-core" -version = "2.4.1" +version = "2.4.2" description = "Google Cloud API client core library" optional = true python-versions = ">=3.7" groups = ["main"] markers = "extra == \"vertexai\"" files = [ - {file = "google-cloud-core-2.4.1.tar.gz", hash = "sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073"}, - {file = "google_cloud_core-2.4.1-py2.py3-none-any.whl", hash = "sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61"}, + {file = "google_cloud_core-2.4.2-py2.py3-none-any.whl", hash = "sha256:7459c3e83de7cb8b9ecfec9babc910efb4314030c56dd798eaad12c426f7d180"}, + {file = "google_cloud_core-2.4.2.tar.gz", hash = "sha256:a4fcb0e2fcfd4bfe963837fad6d10943754fd79c1a50097d68540b6eb3d67f35"}, ] [package.dependencies] @@ -1485,23 +1895,23 @@ grpc = ["grpcio (>=1.38.0,<2.0dev)", "grpcio-status (>=1.38.0,<2.0.dev0)"] [[package]] name = "google-cloud-resource-manager" -version = "1.14.0" +version = "1.14.1" description = "Google Cloud Resource Manager API client library" optional = true python-versions = ">=3.7" groups = ["main"] markers = "extra == \"vertexai\"" files = [ - {file = "google_cloud_resource_manager-1.14.0-py2.py3-none-any.whl", hash = "sha256:4860c3ea9ace760b317ea90d4e27f1b32e54ededdcc340a7cb70c8ef238d8f7c"}, - {file = "google_cloud_resource_manager-1.14.0.tar.gz", hash = "sha256:daa70a3a4704759d31f812ed221e3b6f7b660af30c7862e4a0060ea91291db30"}, + {file = "google_cloud_resource_manager-1.14.1-py2.py3-none-any.whl", hash = "sha256:68340599f85ebf07a6e18487e460ea07cc15e132068f6b188786d01c2cf25518"}, + {file = "google_cloud_resource_manager-1.14.1.tar.gz", hash = "sha256:41e9e546aaa03d5160cdfa2341dbe81ef7596706c300a89b94c429f1f3411f87"}, ] [package.dependencies] google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev" -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +grpc-google-iam-v1 = ">=0.14.0,<1.0.0dev" proto-plus = [ - {version = ">=1.22.3,<2.0.0dev", markers = "python_version < \"3.13\""}, + {version = ">=1.22.3,<2.0.0dev"}, {version = ">=1.25.0,<2.0.0dev", markers = "python_version >= \"3.13\""}, ] protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" @@ -1594,15 +2004,15 @@ requests = ["requests (>=2.18.0,<3.0.0dev)"] [[package]] name = "googleapis-common-protos" -version = "1.66.0" +version = "1.68.0" description = "Common protobufs used in Google APIs" optional = true python-versions = ">=3.7" groups = ["main"] markers = "extra == \"vertexai\"" files = [ - {file = "googleapis_common_protos-1.66.0-py2.py3-none-any.whl", hash = "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed"}, - {file = "googleapis_common_protos-1.66.0.tar.gz", hash = "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c"}, + {file = "googleapis_common_protos-1.68.0-py2.py3-none-any.whl", hash = "sha256:aaf179b2f81df26dfadac95def3b16a95064c76a5f45f07e4c68a21bb371c4ac"}, + {file = "googleapis_common_protos-1.68.0.tar.gz", hash = "sha256:95d38161f4f9af0d9423eed8fb7b64ffd2568c3464eb542ff02c5bfa1953ab3c"}, ] [package.dependencies] @@ -1861,7 +2271,7 @@ httpcore = "==1.*" idna = "*" [package.extras] -brotli = ["brotli", "brotlicffi"] +brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] @@ -1882,15 +2292,15 @@ files = [ [[package]] name = "huggingface-hub" -version = "0.28.1" +version = "0.29.1" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" optional = true python-versions = ">=3.8.0" groups = ["main"] markers = "extra == \"sentence-transformers\" or extra == \"cohere\"" files = [ - {file = "huggingface_hub-0.28.1-py3-none-any.whl", hash = "sha256:aa6b9a3ffdae939b72c464dbb0d7f99f56e649b55c3d52406f49e0a5a620c0a7"}, - {file = "huggingface_hub-0.28.1.tar.gz", hash = "sha256:893471090c98e3b6efbdfdacafe4052b20b84d59866fb6f54c33d9af18c303ae"}, + {file = "huggingface_hub-0.29.1-py3-none-any.whl", hash = "sha256:352f69caf16566c7b6de84b54a822f6238e17ddd8ae3da4f8f2272aea5b198d5"}, + {file = "huggingface_hub-0.29.1.tar.gz", hash = "sha256:9524eae42077b8ff4fc459ceb7a514eca1c1232b775276b009709fe2a084f250"}, ] [package.dependencies] @@ -1933,14 +2343,14 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve [[package]] name = "identify" -version = "2.6.7" +version = "2.6.8" description = "File identification library for Python" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "identify-2.6.7-py2.py3-none-any.whl", hash = "sha256:155931cb617a401807b09ecec6635d6c692d180090a1cedca8ef7d58ba5b6aa0"}, - {file = "identify-2.6.7.tar.gz", hash = "sha256:3fa266b42eba321ee0b2bb0936a6a6b9e36a1351cbb69055b3082f4193035684"}, + {file = "identify-2.6.8-py2.py3-none-any.whl", hash = "sha256:83657f0f766a3c8d0eaea16d4ef42494b39b34629a4b3192a9d020d349b3e255"}, + {file = "identify-2.6.8.tar.gz", hash = "sha256:61491417ea2c0c5c670484fd8abbb34de34cdae1e5f39a73ee65e48e4bb663fc"}, ] [package.extras] @@ -1957,11 +2367,116 @@ files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] -markers = {main = "extra == \"openai\" or extra == \"cohere\" or extra == \"mistralai\" or extra == \"sentence-transformers\" or extra == \"vertexai\" or extra == \"voyageai\""} +markers = {main = "(extra == \"openai\" or extra == \"cohere\" or extra == \"mistralai\" or extra == \"sentence-transformers\" or extra == \"vertexai\" or extra == \"voyageai\" or extra == \"ranx\") and (extra == \"openai\" or extra == \"cohere\" or extra == \"mistralai\" or extra == \"sentence-transformers\" or extra == \"vertexai\" or extra == \"voyageai\") or (extra == \"openai\" or extra == \"cohere\" or extra == \"mistralai\" or extra == \"sentence-transformers\" or extra == \"vertexai\" or extra == \"voyageai\" or extra == \"ranx\") and python_version >= \"3.10\""} [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "ijson" +version = "3.3.0" +description = "Iterative JSON parser with standard Python iterator interfaces" +optional = true +python-versions = "*" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "ijson-3.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7f7a5250599c366369fbf3bc4e176f5daa28eb6bc7d6130d02462ed335361675"}, + {file = "ijson-3.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f87a7e52f79059f9c58f6886c262061065eb6f7554a587be7ed3aa63e6b71b34"}, + {file = "ijson-3.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b73b493af9e947caed75d329676b1b801d673b17481962823a3e55fe529c8b8b"}, + {file = "ijson-3.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5576415f3d76290b160aa093ff968f8bf6de7d681e16e463a0134106b506f49"}, + {file = "ijson-3.3.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e9ffe358d5fdd6b878a8a364e96e15ca7ca57b92a48f588378cef315a8b019e"}, + {file = "ijson-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8643c255a25824ddd0895c59f2319c019e13e949dc37162f876c41a283361527"}, + {file = "ijson-3.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:df3ab5e078cab19f7eaeef1d5f063103e1ebf8c26d059767b26a6a0ad8b250a3"}, + {file = "ijson-3.3.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3dc1fb02c6ed0bae1b4bf96971258bf88aea72051b6e4cebae97cff7090c0607"}, + {file = "ijson-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e9afd97339fc5a20f0542c971f90f3ca97e73d3050cdc488d540b63fae45329a"}, + {file = "ijson-3.3.0-cp310-cp310-win32.whl", hash = "sha256:844c0d1c04c40fd1b60f148dc829d3f69b2de789d0ba239c35136efe9a386529"}, + {file = "ijson-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:d654d045adafdcc6c100e8e911508a2eedbd2a1b5f93f930ba13ea67d7704ee9"}, + {file = "ijson-3.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:501dce8eaa537e728aa35810656aa00460a2547dcb60937c8139f36ec344d7fc"}, + {file = "ijson-3.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:658ba9cad0374d37b38c9893f4864f284cdcc7d32041f9808fba8c7bcaadf134"}, + {file = "ijson-3.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2636cb8c0f1023ef16173f4b9a233bcdb1df11c400c603d5f299fac143ca8d70"}, + {file = "ijson-3.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd174b90db68c3bcca273e9391934a25d76929d727dc75224bf244446b28b03b"}, + {file = "ijson-3.3.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97a9aea46e2a8371c4cf5386d881de833ed782901ac9f67ebcb63bb3b7d115af"}, + {file = "ijson-3.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c594c0abe69d9d6099f4ece17763d53072f65ba60b372d8ba6de8695ce6ee39e"}, + {file = "ijson-3.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8e0ff16c224d9bfe4e9e6bd0395826096cda4a3ef51e6c301e1b61007ee2bd24"}, + {file = "ijson-3.3.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0015354011303175eae7e2ef5136414e91de2298e5a2e9580ed100b728c07e51"}, + {file = "ijson-3.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034642558afa57351a0ffe6de89e63907c4cf6849070cc10a3b2542dccda1afe"}, + {file = "ijson-3.3.0-cp311-cp311-win32.whl", hash = "sha256:192e4b65495978b0bce0c78e859d14772e841724d3269fc1667dc6d2f53cc0ea"}, + {file = "ijson-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:72e3488453754bdb45c878e31ce557ea87e1eb0f8b4fc610373da35e8074ce42"}, + {file = "ijson-3.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:988e959f2f3d59ebd9c2962ae71b97c0df58323910d0b368cc190ad07429d1bb"}, + {file = "ijson-3.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b2f73f0d0fce5300f23a1383d19b44d103bb113b57a69c36fd95b7c03099b181"}, + {file = "ijson-3.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0ee57a28c6bf523d7cb0513096e4eb4dac16cd935695049de7608ec110c2b751"}, + {file = "ijson-3.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0155a8f079c688c2ccaea05de1ad69877995c547ba3d3612c1c336edc12a3a5"}, + {file = "ijson-3.3.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ab00721304af1ae1afa4313ecfa1bf16b07f55ef91e4a5b93aeaa3e2bd7917c"}, + {file = "ijson-3.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40ee3821ee90be0f0e95dcf9862d786a7439bd1113e370736bfdf197e9765bfb"}, + {file = "ijson-3.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3b6987a0bc3e6d0f721b42c7a0198ef897ae50579547b0345f7f02486898f5"}, + {file = "ijson-3.3.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:63afea5f2d50d931feb20dcc50954e23cef4127606cc0ecf7a27128ed9f9a9e6"}, + {file = "ijson-3.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b5c3e285e0735fd8c5a26d177eca8b52512cdd8687ca86ec77a0c66e9c510182"}, + {file = "ijson-3.3.0-cp312-cp312-win32.whl", hash = "sha256:907f3a8674e489abdcb0206723e5560a5cb1fa42470dcc637942d7b10f28b695"}, + {file = "ijson-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:8f890d04ad33262d0c77ead53c85f13abfb82f2c8f078dfbf24b78f59534dfdd"}, + {file = "ijson-3.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b9d85a02e77ee8ea6d9e3fd5d515bcc3d798d9c1ea54817e5feb97a9bc5d52fe"}, + {file = "ijson-3.3.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6576cdc36d5a09b0c1a3d81e13a45d41a6763188f9eaae2da2839e8a4240bce"}, + {file = "ijson-3.3.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5589225c2da4bb732c9c370c5961c39a6db72cf69fb2a28868a5413ed7f39e6"}, + {file = "ijson-3.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad04cf38164d983e85f9cba2804566c0160b47086dcca4cf059f7e26c5ace8ca"}, + {file = "ijson-3.3.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:a3b730ef664b2ef0e99dec01b6573b9b085c766400af363833e08ebc1e38eb2f"}, + {file = "ijson-3.3.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:4690e3af7b134298055993fcbea161598d23b6d3ede11b12dca6815d82d101d5"}, + {file = "ijson-3.3.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:aaa6bfc2180c31a45fac35d40e3312a3d09954638ce0b2e9424a88e24d262a13"}, + {file = "ijson-3.3.0-cp36-cp36m-win32.whl", hash = "sha256:44367090a5a876809eb24943f31e470ba372aaa0d7396b92b953dda953a95d14"}, + {file = "ijson-3.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7e2b3e9ca957153557d06c50a26abaf0d0d6c0ddf462271854c968277a6b5372"}, + {file = "ijson-3.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:47c144117e5c0e2babb559bc8f3f76153863b8dd90b2d550c51dab5f4b84a87f"}, + {file = "ijson-3.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29ce02af5fbf9ba6abb70765e66930aedf73311c7d840478f1ccecac53fefbf3"}, + {file = "ijson-3.3.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ac6c3eeed25e3e2cb9b379b48196413e40ac4e2239d910bb33e4e7f6c137745"}, + {file = "ijson-3.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d92e339c69b585e7b1d857308ad3ca1636b899e4557897ccd91bb9e4a56c965b"}, + {file = "ijson-3.3.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:8c85447569041939111b8c7dbf6f8fa7a0eb5b2c4aebb3c3bec0fb50d7025121"}, + {file = "ijson-3.3.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:542c1e8fddf082159a5d759ee1412c73e944a9a2412077ed00b303ff796907dc"}, + {file = "ijson-3.3.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:30cfea40936afb33b57d24ceaf60d0a2e3d5c1f2335ba2623f21d560737cc730"}, + {file = "ijson-3.3.0-cp37-cp37m-win32.whl", hash = "sha256:6b661a959226ad0d255e49b77dba1d13782f028589a42dc3172398dd3814c797"}, + {file = "ijson-3.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:0b003501ee0301dbf07d1597482009295e16d647bb177ce52076c2d5e64113e0"}, + {file = "ijson-3.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3e8d8de44effe2dbd0d8f3eb9840344b2d5b4cc284a14eb8678aec31d1b6bea8"}, + {file = "ijson-3.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9cd5c03c63ae06d4f876b9844c5898d0044c7940ff7460db9f4cd984ac7862b5"}, + {file = "ijson-3.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04366e7e4a4078d410845e58a2987fd9c45e63df70773d7b6e87ceef771b51ee"}, + {file = "ijson-3.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de7c1ddb80fa7a3ab045266dca169004b93f284756ad198306533b792774f10a"}, + {file = "ijson-3.3.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8851584fb931cffc0caa395f6980525fd5116eab8f73ece9d95e6f9c2c326c4c"}, + {file = "ijson-3.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdcfc88347fd981e53c33d832ce4d3e981a0d696b712fbcb45dcc1a43fe65c65"}, + {file = "ijson-3.3.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3917b2b3d0dbbe3296505da52b3cb0befbaf76119b2edaff30bd448af20b5400"}, + {file = "ijson-3.3.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:e10c14535abc7ddf3fd024aa36563cd8ab5d2bb6234a5d22c77c30e30fa4fb2b"}, + {file = "ijson-3.3.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3aba5c4f97f4e2ce854b5591a8b0711ca3b0c64d1b253b04ea7b004b0a197ef6"}, + {file = "ijson-3.3.0-cp38-cp38-win32.whl", hash = "sha256:b325f42e26659df1a0de66fdb5cde8dd48613da9c99c07d04e9fb9e254b7ee1c"}, + {file = "ijson-3.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:ff835906f84451e143f31c4ce8ad73d83ef4476b944c2a2da91aec8b649570e1"}, + {file = "ijson-3.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3c556f5553368dff690c11d0a1fb435d4ff1f84382d904ccc2dc53beb27ba62e"}, + {file = "ijson-3.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e4396b55a364a03ff7e71a34828c3ed0c506814dd1f50e16ebed3fc447d5188e"}, + {file = "ijson-3.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e6850ae33529d1e43791b30575070670070d5fe007c37f5d06aebc1dd152ab3f"}, + {file = "ijson-3.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36aa56d68ea8def26778eb21576ae13f27b4a47263a7a2581ab2ef58b8de4451"}, + {file = "ijson-3.3.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7ec759c4a0fc820ad5dc6a58e9c391e7b16edcb618056baedbedbb9ea3b1524"}, + {file = "ijson-3.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b51bab2c4e545dde93cb6d6bb34bf63300b7cd06716f195dd92d9255df728331"}, + {file = "ijson-3.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:92355f95a0e4da96d4c404aa3cff2ff033f9180a9515f813255e1526551298c1"}, + {file = "ijson-3.3.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8795e88adff5aa3c248c1edce932db003d37a623b5787669ccf205c422b91e4a"}, + {file = "ijson-3.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8f83f553f4cde6d3d4eaf58ec11c939c94a0ec545c5b287461cafb184f4b3a14"}, + {file = "ijson-3.3.0-cp39-cp39-win32.whl", hash = "sha256:ead50635fb56577c07eff3e557dac39533e0fe603000684eea2af3ed1ad8f941"}, + {file = "ijson-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:c8a9befb0c0369f0cf5c1b94178d0d78f66d9cebb9265b36be6e4f66236076b8"}, + {file = "ijson-3.3.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2af323a8aec8a50fa9effa6d640691a30a9f8c4925bd5364a1ca97f1ac6b9b5c"}, + {file = "ijson-3.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f64f01795119880023ba3ce43072283a393f0b90f52b66cc0ea1a89aa64a9ccb"}, + {file = "ijson-3.3.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a716e05547a39b788deaf22725490855337fc36613288aa8ae1601dc8c525553"}, + {file = "ijson-3.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:473f5d921fadc135d1ad698e2697025045cd8ed7e5e842258295012d8a3bc702"}, + {file = "ijson-3.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd26b396bc3a1e85f4acebeadbf627fa6117b97f4c10b177d5779577c6607744"}, + {file = "ijson-3.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:25fd49031cdf5fd5f1fd21cb45259a64dad30b67e64f745cc8926af1c8c243d3"}, + {file = "ijson-3.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b72178b1e565d06ab19319965022b36ef41bcea7ea153b32ec31194bec032a2"}, + {file = "ijson-3.3.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d0b6b637d05dbdb29d0bfac2ed8425bb369e7af5271b0cc7cf8b801cb7360c2"}, + {file = "ijson-3.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5378d0baa59ae422905c5f182ea0fd74fe7e52a23e3821067a7d58c8306b2191"}, + {file = "ijson-3.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:99f5c8ab048ee4233cc4f2b461b205cbe01194f6201018174ac269bf09995749"}, + {file = "ijson-3.3.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:45ff05de889f3dc3d37a59d02096948ce470699f2368b32113954818b21aa74a"}, + {file = "ijson-3.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efb521090dd6cefa7aafd120581947b29af1713c902ff54336b7c7130f04c47"}, + {file = "ijson-3.3.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87c727691858fd3a1c085d9980d12395517fcbbf02c69fbb22dede8ee03422da"}, + {file = "ijson-3.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0420c24e50389bc251b43c8ed379ab3e3ba065ac8262d98beb6735ab14844460"}, + {file = "ijson-3.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8fdf3721a2aa7d96577970f5604bd81f426969c1822d467f07b3d844fa2fecc7"}, + {file = "ijson-3.3.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:891f95c036df1bc95309951940f8eea8537f102fa65715cdc5aae20b8523813b"}, + {file = "ijson-3.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed1336a2a6e5c427f419da0154e775834abcbc8ddd703004108121c6dd9eba9d"}, + {file = "ijson-3.3.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0c819f83e4f7b7f7463b2dc10d626a8be0c85fbc7b3db0edc098c2b16ac968e"}, + {file = "ijson-3.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33afc25057377a6a43c892de34d229a86f89ea6c4ca3dd3db0dcd17becae0dbb"}, + {file = "ijson-3.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7914d0cf083471856e9bc2001102a20f08e82311dfc8cf1a91aa422f9414a0d6"}, + {file = "ijson-3.3.0.tar.gz", hash = "sha256:7f172e6ba1bee0d4c8f8ebd639577bfe429dee0f3f96775a067b8bae4492d8a0"}, +] + [[package]] name = "imagesize" version = "1.4.1" @@ -1991,12 +2506,12 @@ markers = {dev = "python_version < \"3.10\""} zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -2011,6 +2526,26 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "inscriptis" +version = "2.5.3" +description = "inscriptis - HTML to text converter." +optional = true +python-versions = "<4.0,>=3.9" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "inscriptis-2.5.3-py3-none-any.whl", hash = "sha256:25962cf5a60b1a8f33e7bfbbea08a29af82299702339b9b90c538653a5c7aa38"}, + {file = "inscriptis-2.5.3.tar.gz", hash = "sha256:256043caa13e4995c71fafdeadec4ac42b57f3914cb41023ecbee8bc27ca1cc0"}, +] + +[package.dependencies] +lxml = ">=4.9.3" +requests = ">=2.32.2" + +[package.extras] +web-service = ["fastapi (>=0.109.1,<0.110.0)", "uvicorn (>=0.27.1,<0.28.0)"] + [[package]] name = "ipykernel" version = "6.29.5" @@ -2083,6 +2618,35 @@ qtconsole = ["qtconsole"] test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] +[[package]] +name = "ir-datasets" +version = "0.5.9" +description = "provides a common interface to many IR ad-hoc ranking benchmarks, training datasets, etc." +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "ir_datasets-0.5.9-py3-none-any.whl", hash = "sha256:07c9bed07f31031f1da1bc02afc7a1077b1179a3af402d061f83bf6fb833b90a"}, + {file = "ir_datasets-0.5.9.tar.gz", hash = "sha256:35c90980fbd0f4ea8fe22a1ab16d2bb6be3dc373cbd6dfab1d905f176a70e5ac"}, +] + +[package.dependencies] +beautifulsoup4 = ">=4.4.1" +ijson = ">=3.1.3" +inscriptis = ">=2.2.0" +lxml = ">=4.5.2" +lz4 = ">=3.1.10" +numpy = ">=1.18.1" +pyyaml = ">=5.3.1" +requests = ">=2.22.0" +tqdm = ">=4.38.0" +trec-car-tools = ">=2.5.4" +unlzw3 = ">=0.2.1" +warc3-wet = ">=0.2.3" +warc3-wet-clueweb09 = ">=0.2.5" +zlib-state = ">=0.1.3" + [[package]] name = "isort" version = "5.13.2" @@ -2243,12 +2807,28 @@ description = "Lightweight pipelining with Python functions" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"sentence-transformers\"" +markers = "extra == \"nltk\" or extra == \"sentence-transformers\"" files = [ {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, ] +[[package]] +name = "jsonpath-ng" +version = "1.7.0" +description = "A final implementation of JSONPath for Python that aims to be standard compliant, including arithmetic and binary comparison operators and providing clear AST for metaprogramming." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "jsonpath-ng-1.7.0.tar.gz", hash = "sha256:f6f5f7fd4e5ff79c785f1573b394043b39849fb2bb47bcead935d12b00beab3c"}, + {file = "jsonpath_ng-1.7.0-py2-none-any.whl", hash = "sha256:898c93fc173f0c336784a3fa63d7434297544b7198124a68f9a3ef9597b0ae6e"}, + {file = "jsonpath_ng-1.7.0-py3-none-any.whl", hash = "sha256:f3d7f9e848cba1b6da28c55b1c26ff915dc9e0b1ba7e752a53d6da8d5cbd00b6"}, +] + +[package.dependencies] +ply = "*" + [[package]] name = "jsonpath-python" version = "1.0.6" @@ -2349,7 +2929,7 @@ traitlets = ">=5.3" [package.extras] docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko ; sys_platform == \"win32\"", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] [[package]] name = "jupyter-core" @@ -2384,17 +2964,344 @@ files = [ {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"}, ] +[[package]] +name = "kiwisolver" +version = "1.4.8" +description = "A fast implementation of the Cassowary constraint solver" +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db"}, + {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b"}, + {file = "kiwisolver-1.4.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce2cf1e5688edcb727fdf7cd1bbd0b6416758996826a8be1d958f91880d0809d"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c8bf637892dc6e6aad2bc6d4d69d08764166e5e3f69d469e55427b6ac001b19d"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:034d2c891f76bd3edbdb3ea11140d8510dca675443da7304205a2eaa45d8334c"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47b28d1dfe0793d5e96bce90835e17edf9a499b53969b03c6c47ea5985844c3"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb158fe28ca0c29f2260cca8c43005329ad58452c36f0edf298204de32a9a3ed"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5536185fce131780ebd809f8e623bf4030ce1b161353166c49a3c74c287897f"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:369b75d40abedc1da2c1f4de13f3482cb99e3237b38726710f4a793432b1c5ff"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:641f2ddf9358c80faa22e22eb4c9f54bd3f0e442e038728f500e3b978d00aa7d"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d561d2d8883e0819445cfe58d7ddd673e4015c3c57261d7bdcd3710d0d14005c"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1732e065704b47c9afca7ffa272f845300a4eb959276bf6970dc07265e73b605"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bcb1ebc3547619c3b58a39e2448af089ea2ef44b37988caf432447374941574e"}, + {file = "kiwisolver-1.4.8-cp310-cp310-win_amd64.whl", hash = "sha256:89c107041f7b27844179ea9c85d6da275aa55ecf28413e87624d033cf1f6b751"}, + {file = "kiwisolver-1.4.8-cp310-cp310-win_arm64.whl", hash = "sha256:b5773efa2be9eb9fcf5415ea3ab70fc785d598729fd6057bea38d539ead28271"}, + {file = "kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84"}, + {file = "kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561"}, + {file = "kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67"}, + {file = "kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34"}, + {file = "kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2"}, + {file = "kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502"}, + {file = "kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31"}, + {file = "kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8"}, + {file = "kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50"}, + {file = "kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476"}, + {file = "kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09"}, + {file = "kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1"}, + {file = "kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb"}, + {file = "kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2"}, + {file = "kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e7a019419b7b510f0f7c9dceff8c5eae2392037eae483a7f9162625233802b0a"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:286b18e86682fd2217a48fc6be6b0f20c1d0ed10958d8dc53453ad58d7be0bf8"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4191ee8dfd0be1c3666ccbac178c5a05d5f8d689bbe3fc92f3c4abec817f8fe0"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd2785b9391f2873ad46088ed7599a6a71e762e1ea33e87514b1a441ed1da1c"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c07b29089b7ba090b6f1a669f1411f27221c3662b3a1b7010e67b59bb5a6f10b"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:65ea09a5a3faadd59c2ce96dc7bf0f364986a315949dc6374f04396b0d60e09b"}, + {file = "kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e"}, +] + +[[package]] +name = "llvmlite" +version = "0.44.0" +description = "lightweight wrapper around basic LLVM functionality" +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "llvmlite-0.44.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:9fbadbfba8422123bab5535b293da1cf72f9f478a65645ecd73e781f962ca614"}, + {file = "llvmlite-0.44.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cccf8eb28f24840f2689fb1a45f9c0f7e582dd24e088dcf96e424834af11f791"}, + {file = "llvmlite-0.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7202b678cdf904823c764ee0fe2dfe38a76981f4c1e51715b4cb5abb6cf1d9e8"}, + {file = "llvmlite-0.44.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40526fb5e313d7b96bda4cbb2c85cd5374e04d80732dd36a282d72a560bb6408"}, + {file = "llvmlite-0.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:41e3839150db4330e1b2716c0be3b5c4672525b4c9005e17c7597f835f351ce2"}, + {file = "llvmlite-0.44.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:eed7d5f29136bda63b6d7804c279e2b72e08c952b7c5df61f45db408e0ee52f3"}, + {file = "llvmlite-0.44.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ace564d9fa44bb91eb6e6d8e7754977783c68e90a471ea7ce913bff30bd62427"}, + {file = "llvmlite-0.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5d22c3bfc842668168a786af4205ec8e3ad29fb1bc03fd11fd48460d0df64c1"}, + {file = "llvmlite-0.44.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f01a394e9c9b7b1d4e63c327b096d10f6f0ed149ef53d38a09b3749dcf8c9610"}, + {file = "llvmlite-0.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8489634d43c20cd0ad71330dde1d5bc7b9966937a263ff1ec1cebb90dc50955"}, + {file = "llvmlite-0.44.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:1d671a56acf725bf1b531d5ef76b86660a5ab8ef19bb6a46064a705c6ca80aad"}, + {file = "llvmlite-0.44.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f79a728e0435493611c9f405168682bb75ffd1fbe6fc360733b850c80a026db"}, + {file = "llvmlite-0.44.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0143a5ef336da14deaa8ec26c5449ad5b6a2b564df82fcef4be040b9cacfea9"}, + {file = "llvmlite-0.44.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d752f89e31b66db6f8da06df8b39f9b91e78c5feea1bf9e8c1fba1d1c24c065d"}, + {file = "llvmlite-0.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:eae7e2d4ca8f88f89d315b48c6b741dcb925d6a1042da694aa16ab3dd4cbd3a1"}, + {file = "llvmlite-0.44.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:319bddd44e5f71ae2689859b7203080716448a3cd1128fb144fe5c055219d516"}, + {file = "llvmlite-0.44.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c58867118bad04a0bb22a2e0068c693719658105e40009ffe95c7000fcde88e"}, + {file = "llvmlite-0.44.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46224058b13c96af1365290bdfebe9a6264ae62fb79b2b55693deed11657a8bf"}, + {file = "llvmlite-0.44.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0097052c32bf721a4efc03bd109d335dfa57d9bffb3d4c24cc680711b8b4fc"}, + {file = "llvmlite-0.44.0-cp313-cp313-win_amd64.whl", hash = "sha256:2fb7c4f2fb86cbae6dca3db9ab203eeea0e22d73b99bc2341cdf9de93612e930"}, + {file = "llvmlite-0.44.0.tar.gz", hash = "sha256:07667d66a5d150abed9157ab6c0b9393c9356f229784a4385c02f99e94fc94d4"}, +] + +[[package]] +name = "lxml" +version = "5.3.1" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +optional = true +python-versions = ">=3.6" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "lxml-5.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a4058f16cee694577f7e4dd410263cd0ef75644b43802a689c2b3c2a7e69453b"}, + {file = "lxml-5.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:364de8f57d6eda0c16dcfb999af902da31396949efa0e583e12675d09709881b"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:528f3a0498a8edc69af0559bdcf8a9f5a8bf7c00051a6ef3141fdcf27017bbf5"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db4743e30d6f5f92b6d2b7c86b3ad250e0bad8dee4b7ad8a0c44bfb276af89a3"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17b5d7f8acf809465086d498d62a981fa6a56d2718135bb0e4aa48c502055f5c"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:928e75a7200a4c09e6efc7482a1337919cc61fe1ba289f297827a5b76d8969c2"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a997b784a639e05b9d4053ef3b20c7e447ea80814a762f25b8ed5a89d261eac"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7b82e67c5feb682dbb559c3e6b78355f234943053af61606af126df2183b9ef9"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:f1de541a9893cf8a1b1db9bf0bf670a2decab42e3e82233d36a74eda7822b4c9"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:de1fc314c3ad6bc2f6bd5b5a5b9357b8c6896333d27fdbb7049aea8bd5af2d79"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7c0536bd9178f754b277a3e53f90f9c9454a3bd108b1531ffff720e082d824f2"}, + {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68018c4c67d7e89951a91fbd371e2e34cd8cfc71f0bb43b5332db38497025d51"}, + {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa826340a609d0c954ba52fd831f0fba2a4165659ab0ee1a15e4aac21f302406"}, + {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:796520afa499732191e39fc95b56a3b07f95256f2d22b1c26e217fb69a9db5b5"}, + {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3effe081b3135237da6e4c4530ff2a868d3f80be0bda027e118a5971285d42d0"}, + {file = "lxml-5.3.1-cp310-cp310-win32.whl", hash = "sha256:a22f66270bd6d0804b02cd49dae2b33d4341015545d17f8426f2c4e22f557a23"}, + {file = "lxml-5.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:0bcfadea3cdc68e678d2b20cb16a16716887dd00a881e16f7d806c2138b8ff0c"}, + {file = "lxml-5.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e220f7b3e8656ab063d2eb0cd536fafef396829cafe04cb314e734f87649058f"}, + {file = "lxml-5.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f2cfae0688fd01f7056a17367e3b84f37c545fb447d7282cf2c242b16262607"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67d2f8ad9dcc3a9e826bdc7802ed541a44e124c29b7d95a679eeb58c1c14ade8"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db0c742aad702fd5d0c6611a73f9602f20aec2007c102630c06d7633d9c8f09a"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:198bb4b4dd888e8390afa4f170d4fa28467a7eaf857f1952589f16cfbb67af27"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2a3e412ce1849be34b45922bfef03df32d1410a06d1cdeb793a343c2f1fd666"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b8969dbc8d09d9cd2ae06362c3bad27d03f433252601ef658a49bd9f2b22d79"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5be8f5e4044146a69c96077c7e08f0709c13a314aa5315981185c1f00235fe65"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:133f3493253a00db2c870d3740bc458ebb7d937bd0a6a4f9328373e0db305709"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:52d82b0d436edd6a1d22d94a344b9a58abd6c68c357ed44f22d4ba8179b37629"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b6f92e35e2658a5ed51c6634ceb5ddae32053182851d8cad2a5bc102a359b33"}, + {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:203b1d3eaebd34277be06a3eb880050f18a4e4d60861efba4fb946e31071a295"}, + {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:155e1a5693cf4b55af652f5c0f78ef36596c7f680ff3ec6eb4d7d85367259b2c"}, + {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22ec2b3c191f43ed21f9545e9df94c37c6b49a5af0a874008ddc9132d49a2d9c"}, + {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7eda194dd46e40ec745bf76795a7cccb02a6a41f445ad49d3cf66518b0bd9cff"}, + {file = "lxml-5.3.1-cp311-cp311-win32.whl", hash = "sha256:fb7c61d4be18e930f75948705e9718618862e6fc2ed0d7159b2262be73f167a2"}, + {file = "lxml-5.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c809eef167bf4a57af4b03007004896f5c60bd38dc3852fcd97a26eae3d4c9e6"}, + {file = "lxml-5.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e69add9b6b7b08c60d7ff0152c7c9a6c45b4a71a919be5abde6f98f1ea16421c"}, + {file = "lxml-5.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4e52e1b148867b01c05e21837586ee307a01e793b94072d7c7b91d2c2da02ffe"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4b382e0e636ed54cd278791d93fe2c4f370772743f02bcbe431a160089025c9"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2e49dc23a10a1296b04ca9db200c44d3eb32c8d8ec532e8c1fd24792276522a"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4399b4226c4785575fb20998dc571bc48125dc92c367ce2602d0d70e0c455eb0"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5412500e0dc5481b1ee9cf6b38bb3b473f6e411eb62b83dc9b62699c3b7b79f7"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c93ed3c998ea8472be98fb55aed65b5198740bfceaec07b2eba551e55b7b9ae"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:63d57fc94eb0bbb4735e45517afc21ef262991d8758a8f2f05dd6e4174944519"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:b450d7cabcd49aa7ab46a3c6aa3ac7e1593600a1a0605ba536ec0f1b99a04322"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:4df0ec814b50275ad6a99bc82a38b59f90e10e47714ac9871e1b223895825468"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d184f85ad2bb1f261eac55cddfcf62a70dee89982c978e92b9a74a1bfef2e367"}, + {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b725e70d15906d24615201e650d5b0388b08a5187a55f119f25874d0103f90dd"}, + {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a31fa7536ec1fb7155a0cd3a4e3d956c835ad0a43e3610ca32384d01f079ea1c"}, + {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c3c8b55c7fc7b7e8877b9366568cc73d68b82da7fe33d8b98527b73857a225f"}, + {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d61ec60945d694df806a9aec88e8f29a27293c6e424f8ff91c80416e3c617645"}, + {file = "lxml-5.3.1-cp312-cp312-win32.whl", hash = "sha256:f4eac0584cdc3285ef2e74eee1513a6001681fd9753b259e8159421ed28a72e5"}, + {file = "lxml-5.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:29bfc8d3d88e56ea0a27e7c4897b642706840247f59f4377d81be8f32aa0cfbf"}, + {file = "lxml-5.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c093c7088b40d8266f57ed71d93112bd64c6724d31f0794c1e52cc4857c28e0e"}, + {file = "lxml-5.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0884e3f22d87c30694e625b1e62e6f30d39782c806287450d9dc2fdf07692fd"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1637fa31ec682cd5760092adfabe86d9b718a75d43e65e211d5931809bc111e7"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a364e8e944d92dcbf33b6b494d4e0fb3499dcc3bd9485beb701aa4b4201fa414"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:779e851fd0e19795ccc8a9bb4d705d6baa0ef475329fe44a13cf1e962f18ff1e"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c4393600915c308e546dc7003d74371744234e8444a28622d76fe19b98fa59d1"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:673b9d8e780f455091200bba8534d5f4f465944cbdd61f31dc832d70e29064a5"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2e4a570f6a99e96c457f7bec5ad459c9c420ee80b99eb04cbfcfe3fc18ec6423"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:71f31eda4e370f46af42fc9f264fafa1b09f46ba07bdbee98f25689a04b81c20"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:42978a68d3825eaac55399eb37a4d52012a205c0c6262199b8b44fcc6fd686e8"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8b1942b3e4ed9ed551ed3083a2e6e0772de1e5e3aca872d955e2e86385fb7ff9"}, + {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85c4f11be9cf08917ac2a5a8b6e1ef63b2f8e3799cec194417e76826e5f1de9c"}, + {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:231cf4d140b22a923b1d0a0a4e0b4f972e5893efcdec188934cc65888fd0227b"}, + {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5865b270b420eda7b68928d70bb517ccbe045e53b1a428129bb44372bf3d7dd5"}, + {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7bebc2275016cddf3c997bf8a0f7044160714c64a9b83975670a04e6d2252"}, + {file = "lxml-5.3.1-cp313-cp313-win32.whl", hash = "sha256:d0751528b97d2b19a388b302be2a0ee05817097bab46ff0ed76feeec24951f78"}, + {file = "lxml-5.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:91fb6a43d72b4f8863d21f347a9163eecbf36e76e2f51068d59cd004c506f332"}, + {file = "lxml-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:016b96c58e9a4528219bb563acf1aaaa8bc5452e7651004894a973f03b84ba81"}, + {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82a4bb10b0beef1434fb23a09f001ab5ca87895596b4581fd53f1e5145a8934a"}, + {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d68eeef7b4d08a25e51897dac29bcb62aba830e9ac6c4e3297ee7c6a0cf6439"}, + {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:f12582b8d3b4c6be1d298c49cb7ae64a3a73efaf4c2ab4e37db182e3545815ac"}, + {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2df7ed5edeb6bd5590914cd61df76eb6cce9d590ed04ec7c183cf5509f73530d"}, + {file = "lxml-5.3.1-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:585c4dc429deebc4307187d2b71ebe914843185ae16a4d582ee030e6cfbb4d8a"}, + {file = "lxml-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:06a20d607a86fccab2fc15a77aa445f2bdef7b49ec0520a842c5c5afd8381576"}, + {file = "lxml-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:057e30d0012439bc54ca427a83d458752ccda725c1c161cc283db07bcad43cf9"}, + {file = "lxml-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4867361c049761a56bd21de507cab2c2a608c55102311d142ade7dab67b34f32"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dddf0fb832486cc1ea71d189cb92eb887826e8deebe128884e15020bb6e3f61"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bcc211542f7af6f2dfb705f5f8b74e865592778e6cafdfd19c792c244ccce19"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaca5a812f050ab55426c32177091130b1e49329b3f002a32934cd0245571307"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:236610b77589faf462337b3305a1be91756c8abc5a45ff7ca8f245a71c5dab70"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:aed57b541b589fa05ac248f4cb1c46cbb432ab82cbd467d1c4f6a2bdc18aecf9"}, + {file = "lxml-5.3.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:75fa3d6946d317ffc7016a6fcc44f42db6d514b7fdb8b4b28cbe058303cb6e53"}, + {file = "lxml-5.3.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:96eef5b9f336f623ffc555ab47a775495e7e8846dde88de5f941e2906453a1ce"}, + {file = "lxml-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:ef45f31aec9be01379fc6c10f1d9c677f032f2bac9383c827d44f620e8a88407"}, + {file = "lxml-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0611da6b07dd3720f492db1b463a4d1175b096b49438761cc9f35f0d9eaaef5"}, + {file = "lxml-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b2aca14c235c7a08558fe0a4786a1a05873a01e86b474dfa8f6df49101853a4e"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae82fce1d964f065c32c9517309f0c7be588772352d2f40b1574a214bd6e6098"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7aae7a3d63b935babfdc6864b31196afd5145878ddd22f5200729006366bc4d5"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8e0d177b1fe251c3b1b914ab64135475c5273c8cfd2857964b2e3bb0fe196a7"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:6c4dd3bfd0c82400060896717dd261137398edb7e524527438c54a8c34f736bf"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f1208c1c67ec9e151d78aa3435aa9b08a488b53d9cfac9b699f15255a3461ef2"}, + {file = "lxml-5.3.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c6aacf00d05b38a5069826e50ae72751cb5bc27bdc4d5746203988e429b385bb"}, + {file = "lxml-5.3.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5881aaa4bf3a2d086c5f20371d3a5856199a0d8ac72dd8d0dbd7a2ecfc26ab73"}, + {file = "lxml-5.3.1-cp38-cp38-win32.whl", hash = "sha256:45fbb70ccbc8683f2fb58bea89498a7274af1d9ec7995e9f4af5604e028233fc"}, + {file = "lxml-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:7512b4d0fc5339d5abbb14d1843f70499cab90d0b864f790e73f780f041615d7"}, + {file = "lxml-5.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5885bc586f1edb48e5d68e7a4b4757b5feb2a496b64f462b4d65950f5af3364f"}, + {file = "lxml-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1b92fe86e04f680b848fff594a908edfa72b31bfc3499ef7433790c11d4c8cd8"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a091026c3bf7519ab1e64655a3f52a59ad4a4e019a6f830c24d6430695b1cf6a"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ffb141361108e864ab5f1813f66e4e1164181227f9b1f105b042729b6c15125"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3715cdf0dd31b836433af9ee9197af10e3df41d273c19bb249230043667a5dfd"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88b72eb7222d918c967202024812c2bfb4048deeb69ca328363fb8e15254c549"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa59974880ab5ad8ef3afaa26f9bda148c5f39e06b11a8ada4660ecc9fb2feb3"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3bb8149840daf2c3f97cebf00e4ed4a65a0baff888bf2605a8d0135ff5cf764e"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:0d6b2fa86becfa81f0a0271ccb9eb127ad45fb597733a77b92e8a35e53414914"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:136bf638d92848a939fd8f0e06fcf92d9f2e4b57969d94faae27c55f3d85c05b"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:89934f9f791566e54c1d92cdc8f8fd0009447a5ecdb1ec6b810d5f8c4955f6be"}, + {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a8ade0363f776f87f982572c2860cc43c65ace208db49c76df0a21dde4ddd16e"}, + {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bfbbab9316330cf81656fed435311386610f78b6c93cc5db4bebbce8dd146675"}, + {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:172d65f7c72a35a6879217bcdb4bb11bc88d55fb4879e7569f55616062d387c2"}, + {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e3c623923967f3e5961d272718655946e5322b8d058e094764180cdee7bab1af"}, + {file = "lxml-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ce0930a963ff593e8bb6fda49a503911accc67dee7e5445eec972668e672a0f0"}, + {file = "lxml-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:f7b64fcd670bca8800bc10ced36620c6bbb321e7bc1214b9c0c0df269c1dddc2"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:afa578b6524ff85fb365f454cf61683771d0170470c48ad9d170c48075f86725"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f5e80adf0aafc7b5454f2c1cb0cde920c9b1f2cbd0485f07cc1d0497c35c5d"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd0b80ac2d8f13ffc906123a6f20b459cb50a99222d0da492360512f3e50f84"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:422c179022ecdedbe58b0e242607198580804253da220e9454ffe848daa1cfd2"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:524ccfded8989a6595dbdda80d779fb977dbc9a7bc458864fc9a0c2fc15dc877"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:48fd46bf7155def2e15287c6f2b133a2f78e2d22cdf55647269977b873c65499"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:05123fad495a429f123307ac6d8fd6f977b71e9a0b6d9aeeb8f80c017cb17131"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a243132767150a44e6a93cd1dde41010036e1cbc63cc3e9fe1712b277d926ce3"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c92ea6d9dd84a750b2bae72ff5e8cf5fdd13e58dda79c33e057862c29a8d5b50"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2f1be45d4c15f237209bbf123a0e05b5d630c8717c42f59f31ea9eae2ad89394"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a83d3adea1e0ee36dac34627f78ddd7f093bb9cfc0a8e97f1572a949b695cb98"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3edbb9c9130bac05d8c3fe150c51c337a471cc7fdb6d2a0a7d3a88e88a829314"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2f23cf50eccb3255b6e913188291af0150d89dab44137a69e14e4dcb7be981f1"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df7e5edac4778127f2bf452e0721a58a1cfa4d1d9eac63bdd650535eb8543615"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:094b28ed8a8a072b9e9e2113a81fda668d2053f2ca9f2d202c2c8c7c2d6516b1"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:514fe78fc4b87e7a7601c92492210b20a1b0c6ab20e71e81307d9c2e377c64de"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8fffc08de02071c37865a155e5ea5fce0282e1546fd5bde7f6149fcaa32558ac"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4b0d5cdba1b655d5b18042ac9c9ff50bda33568eb80feaaca4fc237b9c4fbfde"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3031e4c16b59424e8d78522c69b062d301d951dc55ad8685736c3335a97fc270"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb659702a45136c743bc130760c6f137870d4df3a9e14386478b8a0511abcfca"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a11b16a33656ffc43c92a5343a28dc71eefe460bcc2a4923a96f292692709f6"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c5ae125276f254b01daa73e2c103363d3e99e3e10505686ac7d9d2442dd4627a"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c76722b5ed4a31ba103e0dc77ab869222ec36efe1a614e42e9bcea88a36186fe"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:33e06717c00c788ab4e79bc4726ecc50c54b9bfb55355eae21473c145d83c2d2"}, + {file = "lxml-5.3.1.tar.gz", hash = "sha256:106b7b5d2977b339f1e97efe2778e2ab20e99994cbb0ec5e55771ed0795920c8"}, +] + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html-clean = ["lxml_html_clean"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=3.0.11,<3.1.0)"] + +[[package]] +name = "lz4" +version = "4.4.3" +description = "LZ4 Bindings for Python" +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "lz4-4.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1ebf23ffd36b32b980f720a81990fcfdeadacafe7498fbeff7a8e058259d4e58"}, + {file = "lz4-4.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8fe3caea61427057a9e3697c69b2403510fdccfca4483520d02b98ffae74531e"}, + {file = "lz4-4.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e86c7fbe46f6e2e9dfb5377ee690fb8987e8e8363f435886ab91012b88f08a26"}, + {file = "lz4-4.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a46f48740584eab3194fbee91c61f7fa396dbb1c5e7aa76ca08165d4e63fb40f"}, + {file = "lz4-4.4.3-cp310-cp310-win32.whl", hash = "sha256:434a1d1547a0547164866f1ccc31bbda235ac5b9087f24a84956756b52371f40"}, + {file = "lz4-4.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:0aea6f283abd6acb1883b70d7a117b913e20c770845559f9421394bc9c522b24"}, + {file = "lz4-4.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b1b98f0a4137d01b84c680813eef6198e1e00f1f28bc20ce7b5c436459a0d146"}, + {file = "lz4-4.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20e385cb8bd8321593788f11101d8c89a823a56191978e427e3c5141e129f14b"}, + {file = "lz4-4.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c9e32989df06c57f10aa09ad9b30e8a25baf1aefe850e13b0ea5de600477d6a"}, + {file = "lz4-4.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3d2d5df5476b065aae9d1ad551fdc7b17c151b84e8edd9212108946b2337c66"}, + {file = "lz4-4.4.3-cp311-cp311-win32.whl", hash = "sha256:e365850166729fa82be618f476966161d5c47ea081eafc4febfc542bc85bac5d"}, + {file = "lz4-4.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:7f5c05bd4b0909b682608c453acc31f1a9170d55f56d27cd701213e0683fc66a"}, + {file = "lz4-4.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:43461e439ef71d49bb0ee3a1719494cd952a58d205496698e0cde866f22006bc"}, + {file = "lz4-4.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ae50a175fb7b900f7aa42575f4fe99c32ca0ff57e5a8c1fd25e1243e67409db"}, + {file = "lz4-4.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38df5929ffefa9dda120ba1790a2e94fda81916c5aaa1ee652f4b1e515ebb9ed"}, + {file = "lz4-4.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b45914f25d916324531d0259072b402c5f99b67c6e9ac8cbc3d49935aeb1d97"}, + {file = "lz4-4.4.3-cp312-cp312-win32.whl", hash = "sha256:848c5b040d2cfe35097b1d65d1095d83a3f86374ce879e189533f61405d8763b"}, + {file = "lz4-4.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:b1d179bdefd9ddb8d11d7de7825e73fb957511b722a8cb484e417885c210e68c"}, + {file = "lz4-4.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:174b7ce5456671c73b81bb115defac8a584363d8b38a48ed3ad976e08eea27cd"}, + {file = "lz4-4.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ab26b4af13308b8296688b03d74c3b0c8e8ed1f6b2d1454ef97bdb589db409db"}, + {file = "lz4-4.4.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61e08d84e3bf8ca9f43dc6b33f8cd7ba19f49864e2c91eb2160f83b6f9a268fa"}, + {file = "lz4-4.4.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71ebdaadf546d6d393b9a21796172723724b737e84f68f36caf367d1c87a86a1"}, + {file = "lz4-4.4.3-cp313-cp313-win32.whl", hash = "sha256:1f25e1b571a8be2c3d60d46679ef2471ae565f7ba9ba8382596695413523b188"}, + {file = "lz4-4.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:da091dd8c96dbda124d766231f38619afd5c544051fb4424d2566c905957d342"}, + {file = "lz4-4.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:699d26ac579eb42c71d131f9fb7b6e1c495a14e257264206a3c3bfcc146ed9bb"}, + {file = "lz4-4.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c4be1e5d9c8ad61345730c41c9ef21bdbb022cced4df70431110888d3ad5c0fb"}, + {file = "lz4-4.4.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de86400c8b60c7707665e63934a82ae6792e7102c17a72e9b361a7f40d3c6049"}, + {file = "lz4-4.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe6080299a25fd7cbb1957c921cca6a884acbfcd44cc23de48079389d322e326"}, + {file = "lz4-4.4.3-cp39-cp39-win32.whl", hash = "sha256:447993c4dda0b6b0e1bd862752c855df8745f2910bea5015344f83ff3e99f305"}, + {file = "lz4-4.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:3f21e503c18157512d2e34ae4c301e44a826c7b87e1d8998981367e3c9fe0932"}, + {file = "lz4-4.4.3.tar.gz", hash = "sha256:91ed5b71f9179bf3dbfe85d92b52d4b53de2e559aa4daa3b7de18e0dd24ad77d"}, +] + +[package.extras] +docs = ["sphinx (>=1.6.0)", "sphinx_bootstrap_theme"] +flake8 = ["flake8"] +tests = ["psutil", "pytest (!=3.3.0)", "pytest-cov"] + [[package]] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" -groups = ["docs"] +groups = ["main", "docs"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, ] +markers = {main = "python_version >= \"3.10\" and extra == \"ranx\""} [package.dependencies] mdurl = ">=0.1,<1.0" @@ -2480,6 +3387,65 @@ files = [ {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] +[[package]] +name = "matplotlib" +version = "3.10.1" +description = "Python plotting package" +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "matplotlib-3.10.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ff2ae14910be903f4a24afdbb6d7d3a6c44da210fc7d42790b87aeac92238a16"}, + {file = "matplotlib-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0721a3fd3d5756ed593220a8b86808a36c5031fce489adb5b31ee6dbb47dd5b2"}, + {file = "matplotlib-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0673b4b8f131890eb3a1ad058d6e065fb3c6e71f160089b65f8515373394698"}, + {file = "matplotlib-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e875b95ac59a7908978fe307ecdbdd9a26af7fa0f33f474a27fcf8c99f64a19"}, + {file = "matplotlib-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2589659ea30726284c6c91037216f64a506a9822f8e50592d48ac16a2f29e044"}, + {file = "matplotlib-3.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a97ff127f295817bc34517255c9db6e71de8eddaab7f837b7d341dee9f2f587f"}, + {file = "matplotlib-3.10.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:057206ff2d6ab82ff3e94ebd94463d084760ca682ed5f150817b859372ec4401"}, + {file = "matplotlib-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a144867dd6bf8ba8cb5fc81a158b645037e11b3e5cf8a50bd5f9917cb863adfe"}, + {file = "matplotlib-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56c5d9fcd9879aa8040f196a235e2dcbdf7dd03ab5b07c0696f80bc6cf04bedd"}, + {file = "matplotlib-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f69dc9713e4ad2fb21a1c30e37bd445d496524257dfda40ff4a8efb3604ab5c"}, + {file = "matplotlib-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c59af3e8aca75d7744b68e8e78a669e91ccbcf1ac35d0102a7b1b46883f1dd7"}, + {file = "matplotlib-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:11b65088c6f3dae784bc72e8d039a2580186285f87448babb9ddb2ad0082993a"}, + {file = "matplotlib-3.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:66e907a06e68cb6cfd652c193311d61a12b54f56809cafbed9736ce5ad92f107"}, + {file = "matplotlib-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b4bb156abb8fa5e5b2b460196f7db7264fc6d62678c03457979e7d5254b7be"}, + {file = "matplotlib-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1985ad3d97f51307a2cbfc801a930f120def19ba22864182dacef55277102ba6"}, + {file = "matplotlib-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c96f2c2f825d1257e437a1482c5a2cf4fee15db4261bd6fc0750f81ba2b4ba3d"}, + {file = "matplotlib-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35e87384ee9e488d8dd5a2dd7baf471178d38b90618d8ea147aced4ab59c9bea"}, + {file = "matplotlib-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfd414bce89cc78a7e1d25202e979b3f1af799e416010a20ab2b5ebb3a02425c"}, + {file = "matplotlib-3.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c42eee41e1b60fd83ee3292ed83a97a5f2a8239b10c26715d8a6172226988d7b"}, + {file = "matplotlib-3.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4f0647b17b667ae745c13721602b540f7aadb2a32c5b96e924cd4fea5dcb90f1"}, + {file = "matplotlib-3.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa3854b5f9473564ef40a41bc922be978fab217776e9ae1545c9b3a5cf2092a3"}, + {file = "matplotlib-3.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e496c01441be4c7d5f96d4e40f7fca06e20dcb40e44c8daa2e740e1757ad9e6"}, + {file = "matplotlib-3.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5d45d3f5245be5b469843450617dcad9af75ca50568acf59997bed9311131a0b"}, + {file = "matplotlib-3.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:8e8e25b1209161d20dfe93037c8a7f7ca796ec9aa326e6e4588d8c4a5dd1e473"}, + {file = "matplotlib-3.10.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:19b06241ad89c3ae9469e07d77efa87041eac65d78df4fcf9cac318028009b01"}, + {file = "matplotlib-3.10.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01e63101ebb3014e6e9f80d9cf9ee361a8599ddca2c3e166c563628b39305dbb"}, + {file = "matplotlib-3.10.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f06bad951eea6422ac4e8bdebcf3a70c59ea0a03338c5d2b109f57b64eb3972"}, + {file = "matplotlib-3.10.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfb036f34873b46978f55e240cff7a239f6c4409eac62d8145bad3fc6ba5a3"}, + {file = "matplotlib-3.10.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dc6ab14a7ab3b4d813b88ba957fc05c79493a037f54e246162033591e770de6f"}, + {file = "matplotlib-3.10.1-cp313-cp313t-win_amd64.whl", hash = "sha256:bc411ebd5889a78dabbc457b3fa153203e22248bfa6eedc6797be5df0164dbf9"}, + {file = "matplotlib-3.10.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:648406f1899f9a818cef8c0231b44dcfc4ff36f167101c3fd1c9151f24220fdc"}, + {file = "matplotlib-3.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:02582304e352f40520727984a5a18f37e8187861f954fea9be7ef06569cf85b4"}, + {file = "matplotlib-3.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3809916157ba871bcdd33d3493acd7fe3037db5daa917ca6e77975a94cef779"}, + {file = "matplotlib-3.10.1.tar.gz", hash = "sha256:e8d2d0e3881b129268585bf4765ad3ee73a4591d77b9a18c214ac7e3a79fb2ba"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +kiwisolver = ">=1.3.1" +numpy = ">=1.23" +packaging = ">=20.0" +pillow = ">=8" +pyparsing = ">=2.3.1" +python-dateutil = ">=2.7" + +[package.extras] +dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"] + [[package]] name = "matplotlib-inline" version = "0.1.7" @@ -2533,11 +3499,12 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" -groups = ["docs"] +groups = ["main", "docs"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +markers = {main = "python_version >= \"3.10\" and extra == \"ranx\""} [[package]] name = "mistralai" @@ -2565,14 +3532,14 @@ gcp = ["google-auth (>=2.27.0)", "requests (>=2.32.3)"] [[package]] name = "mistune" -version = "3.1.1" +version = "3.1.2" description = "A sane and fast Markdown parser with useful plugins and renderers" optional = false python-versions = ">=3.8" groups = ["docs"] files = [ - {file = "mistune-3.1.1-py3-none-any.whl", hash = "sha256:02106ac2aa4f66e769debbfa028509a275069dcffce0dfa578edd7b991ee700a"}, - {file = "mistune-3.1.1.tar.gz", hash = "sha256:e0740d635f515119f7d1feb6f9b192ee60f0cc649f80a8f944f905706a21654c"}, + {file = "mistune-3.1.2-py3-none-any.whl", hash = "sha256:4b47731332315cdca99e0ded46fc0004001c1299ff773dfb48fbe1fd226de319"}, + {file = "mistune-3.1.2.tar.gz", hash = "sha256:733bf018ba007e8b5f2d3a9eb624034f6ee26c4ea769a98ec533ee111d504dff"}, ] [package.dependencies] @@ -2607,9 +3574,9 @@ files = [ [package.dependencies] numpy = [ - {version = ">1.20", markers = "python_version < \"3.10\""}, - {version = ">=1.21.2", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, - {version = ">=1.23.3", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">1.20"}, + {version = ">=1.21.2", markers = "python_version >= \"3.10\""}, + {version = ">=1.23.3", markers = "python_version >= \"3.11\""}, {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] @@ -2632,7 +3599,7 @@ files = [ [package.extras] develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] docs = ["sphinx"] -gmpy = ["gmpy2 (>=2.1.0a4)"] +gmpy = ["gmpy2 (>=2.1.0a4) ; platform_python_implementation != \"PyPy\""] tests = ["pytest (>=4.6)"] [[package]] @@ -3012,6 +3979,33 @@ doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9. extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"] test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] +[[package]] +name = "nltk" +version = "3.9.1" +description = "Natural Language Toolkit" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"nltk\"" +files = [ + {file = "nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1"}, + {file = "nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868"}, +] + +[package.dependencies] +click = "*" +joblib = "*" +regex = ">=2021.8.3" +tqdm = "*" + +[package.extras] +all = ["matplotlib", "numpy", "pyparsing", "python-crfsuite", "requests", "scikit-learn", "scipy", "twython"] +corenlp = ["requests"] +machine-learning = ["numpy", "python-crfsuite", "scikit-learn", "scipy"] +plot = ["matplotlib"] +tgrep = ["pyparsing"] +twitter = ["twython"] + [[package]] name = "nodeenv" version = "1.9.1" @@ -3024,6 +4018,42 @@ files = [ {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] +[[package]] +name = "numba" +version = "0.61.0" +description = "compiling Python code using LLVM" +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "numba-0.61.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:9cab9783a700fa428b1a54d65295122bc03b3de1d01fb819a6b9dbbddfdb8c43"}, + {file = "numba-0.61.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:46c5ae094fb3706f5adf9021bfb7fc11e44818d61afee695cdee4eadfed45e98"}, + {file = "numba-0.61.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6fb74e81aa78a2303e30593d8331327dfc0d2522b5db05ac967556a26db3ef87"}, + {file = "numba-0.61.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:0ebbd4827091384ab8c4615ba1b3ca8bc639a3a000157d9c37ba85d34cd0da1b"}, + {file = "numba-0.61.0-cp310-cp310-win_amd64.whl", hash = "sha256:43aa4d7d10c542d3c78106b8481e0cbaaec788c39ee8e3d7901682748ffdf0b4"}, + {file = "numba-0.61.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:bf64c2d0f3d161af603de3825172fb83c2600bcb1d53ae8ea568d4c53ba6ac08"}, + {file = "numba-0.61.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:de5aa7904741425f28e1028b85850b31f0a245e9eb4f7c38507fb893283a066c"}, + {file = "numba-0.61.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21c2fe25019267a608e2710a6a947f557486b4b0478b02e45a81cf606a05a7d4"}, + {file = "numba-0.61.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:74250b26ed6a1428763e774dc5b2d4e70d93f73795635b5412b8346a4d054574"}, + {file = "numba-0.61.0-cp311-cp311-win_amd64.whl", hash = "sha256:b72bbc8708e98b3741ad0c63f9929c47b623cc4ee86e17030a4f3e301e8401ac"}, + {file = "numba-0.61.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:152146ecdbb8d8176f294e9f755411e6f270103a11c3ff50cecc413f794e52c8"}, + {file = "numba-0.61.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5cafa6095716fcb081618c28a8d27bf7c001e09696f595b41836dec114be2905"}, + {file = "numba-0.61.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ffe9fe373ed30638d6e20a0269f817b2c75d447141f55a675bfcf2d1fe2e87fb"}, + {file = "numba-0.61.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9f25f7fef0206d55c1cfb796ad833cbbc044e2884751e56e798351280038484c"}, + {file = "numba-0.61.0-cp312-cp312-win_amd64.whl", hash = "sha256:550d389573bc3b895e1ccb18289feea11d937011de4d278b09dc7ed585d1cdcb"}, + {file = "numba-0.61.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:b96fafbdcf6f69b69855273e988696aae4974115a815f6818fef4af7afa1f6b8"}, + {file = "numba-0.61.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f6c452dca1de8e60e593f7066df052dd8da09b243566ecd26d2b796e5d3087d"}, + {file = "numba-0.61.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:44240e694d4aa321430c97b21453e46014fe6c7b8b7d932afa7f6a88cc5d7e5e"}, + {file = "numba-0.61.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:764f0e47004f126f58c3b28e0a02374c420a9d15157b90806d68590f5c20cc89"}, + {file = "numba-0.61.0-cp313-cp313-win_amd64.whl", hash = "sha256:074cd38c5b1f9c65a4319d1f3928165f48975ef0537ad43385b2bd908e6e2e35"}, + {file = "numba-0.61.0.tar.gz", hash = "sha256:888d2e89b8160899e19591467e8fdd4970e07606e1fbc248f239c89818d5f925"}, +] + +[package.dependencies] +llvmlite = "==0.44.*" +numpy = ">=1.24,<2.2" + [[package]] name = "numpy" version = "1.26.4" @@ -3073,68 +4103,68 @@ files = [ [[package]] name = "numpy" -version = "2.2.2" +version = "2.1.3" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" groups = ["main"] markers = "python_version >= \"3.12\"" files = [ - {file = "numpy-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e"}, - {file = "numpy-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e"}, - {file = "numpy-2.2.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715"}, - {file = "numpy-2.2.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a"}, - {file = "numpy-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97"}, - {file = "numpy-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957"}, - {file = "numpy-2.2.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d"}, - {file = "numpy-2.2.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd"}, - {file = "numpy-2.2.2-cp310-cp310-win32.whl", hash = "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160"}, - {file = "numpy-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014"}, - {file = "numpy-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189"}, - {file = "numpy-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323"}, - {file = "numpy-2.2.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac"}, - {file = "numpy-2.2.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e"}, - {file = "numpy-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c"}, - {file = "numpy-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f"}, - {file = "numpy-2.2.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826"}, - {file = "numpy-2.2.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8"}, - {file = "numpy-2.2.2-cp311-cp311-win32.whl", hash = "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50"}, - {file = "numpy-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2"}, - {file = "numpy-2.2.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467"}, - {file = "numpy-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a"}, - {file = "numpy-2.2.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825"}, - {file = "numpy-2.2.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37"}, - {file = "numpy-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748"}, - {file = "numpy-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0"}, - {file = "numpy-2.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278"}, - {file = "numpy-2.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba"}, - {file = "numpy-2.2.2-cp312-cp312-win32.whl", hash = "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283"}, - {file = "numpy-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb"}, - {file = "numpy-2.2.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc"}, - {file = "numpy-2.2.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369"}, - {file = "numpy-2.2.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd"}, - {file = "numpy-2.2.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be"}, - {file = "numpy-2.2.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84"}, - {file = "numpy-2.2.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff"}, - {file = "numpy-2.2.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0"}, - {file = "numpy-2.2.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de"}, - {file = "numpy-2.2.2-cp313-cp313-win32.whl", hash = "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9"}, - {file = "numpy-2.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369"}, - {file = "numpy-2.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391"}, - {file = "numpy-2.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39"}, - {file = "numpy-2.2.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317"}, - {file = "numpy-2.2.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49"}, - {file = "numpy-2.2.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2"}, - {file = "numpy-2.2.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7"}, - {file = "numpy-2.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb"}, - {file = "numpy-2.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648"}, - {file = "numpy-2.2.2-cp313-cp313t-win32.whl", hash = "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4"}, - {file = "numpy-2.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576"}, - {file = "numpy-2.2.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495"}, - {file = "numpy-2.2.2-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df"}, - {file = "numpy-2.2.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a"}, - {file = "numpy-2.2.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60"}, - {file = "numpy-2.2.2.tar.gz", hash = "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd"}, + {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3"}, + {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098"}, + {file = "numpy-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c"}, + {file = "numpy-2.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4"}, + {file = "numpy-2.1.3-cp310-cp310-win32.whl", hash = "sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23"}, + {file = "numpy-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09"}, + {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a"}, + {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b"}, + {file = "numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee"}, + {file = "numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0"}, + {file = "numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9"}, + {file = "numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564"}, + {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512"}, + {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b"}, + {file = "numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc"}, + {file = "numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0"}, + {file = "numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9"}, + {file = "numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe"}, + {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43"}, + {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56"}, + {file = "numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a"}, + {file = "numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef"}, + {file = "numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f"}, + {file = "numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0"}, + {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408"}, + {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6"}, + {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f"}, + {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17"}, + {file = "numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48"}, + {file = "numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb"}, + {file = "numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"}, ] [[package]] @@ -3332,15 +4362,15 @@ files = [ [[package]] name = "openai" -version = "1.61.1" +version = "1.65.1" description = "The official Python library for the openai API" optional = true python-versions = ">=3.8" groups = ["main"] markers = "extra == \"openai\"" files = [ - {file = "openai-1.61.1-py3-none-any.whl", hash = "sha256:72b0826240ce26026ac2cd17951691f046e5be82ad122d20a8e1b30ca18bd11e"}, - {file = "openai-1.61.1.tar.gz", hash = "sha256:ce1851507218209961f89f3520e06726c0aa7d0512386f0f977e3ac3e4f2472e"}, + {file = "openai-1.65.1-py3-none-any.whl", hash = "sha256:396652a6452dd42791b3ad8a3aab09b1feb7c1c4550a672586fb300760a8e204"}, + {file = "openai-1.65.1.tar.gz", hash = "sha256:9d9370a20d2b8c3ce319fd2194c2eef5eab59effbcc5b04ff480977edc530fba"}, ] [package.dependencies] @@ -3357,6 +4387,96 @@ typing-extensions = ">=4.11,<5" datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] realtime = ["websockets (>=13,<15)"] +[[package]] +name = "orjson" +version = "3.10.15" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "orjson-3.10.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:552c883d03ad185f720d0c09583ebde257e41b9521b74ff40e08b7dec4559c04"}, + {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616e3e8d438d02e4854f70bfdc03a6bcdb697358dbaa6bcd19cbe24d24ece1f8"}, + {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c2c79fa308e6edb0ffab0a31fd75a7841bf2a79a20ef08a3c6e3b26814c8ca8"}, + {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cb85490aa6bf98abd20607ab5c8324c0acb48d6da7863a51be48505646c814"}, + {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763dadac05e4e9d2bc14938a45a2d0560549561287d41c465d3c58aec818b164"}, + {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a330b9b4734f09a623f74a7490db713695e13b67c959713b78369f26b3dee6bf"}, + {file = "orjson-3.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a61a4622b7ff861f019974f73d8165be1bd9a0855e1cad18ee167acacabeb061"}, + {file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acd271247691574416b3228db667b84775c497b245fa275c6ab90dc1ffbbd2b3"}, + {file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4759b109c37f635aa5c5cc93a1b26927bfde24b254bcc0e1149a9fada253d2d"}, + {file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e992fd5cfb8b9f00bfad2fd7a05a4299db2bbe92e6440d9dd2fab27655b3182"}, + {file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f95fb363d79366af56c3f26b71df40b9a583b07bbaaf5b317407c4d58497852e"}, + {file = "orjson-3.10.15-cp310-cp310-win32.whl", hash = "sha256:f9875f5fea7492da8ec2444839dcc439b0ef298978f311103d0b7dfd775898ab"}, + {file = "orjson-3.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:17085a6aa91e1cd70ca8533989a18b5433e15d29c574582f76f821737c8d5806"}, + {file = "orjson-3.10.15-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c4cc83960ab79a4031f3119cc4b1a1c627a3dc09df125b27c4201dff2af7eaa6"}, + {file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddbeef2481d895ab8be5185f2432c334d6dec1f5d1933a9c83014d188e102cef"}, + {file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e590a0477b23ecd5b0ac865b1b907b01b3c5535f5e8a8f6ab0e503efb896334"}, + {file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6be38bd103d2fd9bdfa31c2720b23b5d47c6796bcb1d1b598e3924441b4298d"}, + {file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ff4f6edb1578960ed628a3b998fa54d78d9bb3e2eb2cfc5c2a09732431c678d0"}, + {file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0482b21d0462eddd67e7fce10b89e0b6ac56570424662b685a0d6fccf581e13"}, + {file = "orjson-3.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bb5cc3527036ae3d98b65e37b7986a918955f85332c1ee07f9d3f82f3a6899b5"}, + {file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d569c1c462912acdd119ccbf719cf7102ea2c67dd03b99edcb1a3048651ac96b"}, + {file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1e6d33efab6b71d67f22bf2962895d3dc6f82a6273a965fab762e64fa90dc399"}, + {file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c33be3795e299f565681d69852ac8c1bc5c84863c0b0030b2b3468843be90388"}, + {file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eea80037b9fae5339b214f59308ef0589fc06dc870578b7cce6d71eb2096764c"}, + {file = "orjson-3.10.15-cp311-cp311-win32.whl", hash = "sha256:d5ac11b659fd798228a7adba3e37c010e0152b78b1982897020a8e019a94882e"}, + {file = "orjson-3.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:cf45e0214c593660339ef63e875f32ddd5aa3b4adc15e662cdb80dc49e194f8e"}, + {file = "orjson-3.10.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d11c0714fc85bfcf36ada1179400862da3288fc785c30e8297844c867d7505a"}, + {file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dba5a1e85d554e3897fa9fe6fbcff2ed32d55008973ec9a2b992bd9a65d2352d"}, + {file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7723ad949a0ea502df656948ddd8b392780a5beaa4c3b5f97e525191b102fff0"}, + {file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fd9bc64421e9fe9bd88039e7ce8e58d4fead67ca88e3a4014b143cec7684fd4"}, + {file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dadba0e7b6594216c214ef7894c4bd5f08d7c0135f4dd0145600be4fbcc16767"}, + {file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48f59114fe318f33bbaee8ebeda696d8ccc94c9e90bc27dbe72153094e26f41"}, + {file = "orjson-3.10.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:035fb83585e0f15e076759b6fedaf0abb460d1765b6a36f48018a52858443514"}, + {file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d13b7fe322d75bf84464b075eafd8e7dd9eae05649aa2a5354cfa32f43c59f17"}, + {file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7066b74f9f259849629e0d04db6609db4cf5b973248f455ba5d3bd58a4daaa5b"}, + {file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88dc3f65a026bd3175eb157fea994fca6ac7c4c8579fc5a86fc2114ad05705b7"}, + {file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b342567e5465bd99faa559507fe45e33fc76b9fb868a63f1642c6bc0735ad02a"}, + {file = "orjson-3.10.15-cp312-cp312-win32.whl", hash = "sha256:0a4f27ea5617828e6b58922fdbec67b0aa4bb844e2d363b9244c47fa2180e665"}, + {file = "orjson-3.10.15-cp312-cp312-win_amd64.whl", hash = "sha256:ef5b87e7aa9545ddadd2309efe6824bd3dd64ac101c15dae0f2f597911d46eaa"}, + {file = "orjson-3.10.15-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bae0e6ec2b7ba6895198cd981b7cca95d1487d0147c8ed751e5632ad16f031a6"}, + {file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f93ce145b2db1252dd86af37d4165b6faa83072b46e3995ecc95d4b2301b725a"}, + {file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c203f6f969210128af3acae0ef9ea6aab9782939f45f6fe02d05958fe761ef9"}, + {file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8918719572d662e18b8af66aef699d8c21072e54b6c82a3f8f6404c1f5ccd5e0"}, + {file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f71eae9651465dff70aa80db92586ad5b92df46a9373ee55252109bb6b703307"}, + {file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e117eb299a35f2634e25ed120c37c641398826c2f5a3d3cc39f5993b96171b9e"}, + {file = "orjson-3.10.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13242f12d295e83c2955756a574ddd6741c81e5b99f2bef8ed8d53e47a01e4b7"}, + {file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7946922ada8f3e0b7b958cc3eb22cfcf6c0df83d1fe5521b4a100103e3fa84c8"}, + {file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b7155eb1623347f0f22c38c9abdd738b287e39b9982e1da227503387b81b34ca"}, + {file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:208beedfa807c922da4e81061dafa9c8489c6328934ca2a562efa707e049e561"}, + {file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eca81f83b1b8c07449e1d6ff7074e82e3fd6777e588f1a6632127f286a968825"}, + {file = "orjson-3.10.15-cp313-cp313-win32.whl", hash = "sha256:c03cd6eea1bd3b949d0d007c8d57049aa2b39bd49f58b4b2af571a5d3833d890"}, + {file = "orjson-3.10.15-cp313-cp313-win_amd64.whl", hash = "sha256:fd56a26a04f6ba5fb2045b0acc487a63162a958ed837648c5781e1fe3316cfbf"}, + {file = "orjson-3.10.15-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5e8afd6200e12771467a1a44e5ad780614b86abb4b11862ec54861a82d677746"}, + {file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9a18c500f19273e9e104cca8c1f0b40a6470bcccfc33afcc088045d0bf5ea6"}, + {file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb00b7bfbdf5d34a13180e4805d76b4567025da19a197645ca746fc2fb536586"}, + {file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33aedc3d903378e257047fee506f11e0833146ca3e57a1a1fb0ddb789876c1e1"}, + {file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd0099ae6aed5eb1fc84c9eb72b95505a3df4267e6962eb93cdd5af03be71c98"}, + {file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c864a80a2d467d7786274fce0e4f93ef2a7ca4ff31f7fc5634225aaa4e9e98c"}, + {file = "orjson-3.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c25774c9e88a3e0013d7d1a6c8056926b607a61edd423b50eb5c88fd7f2823ae"}, + {file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e78c211d0074e783d824ce7bb85bf459f93a233eb67a5b5003498232ddfb0e8a"}, + {file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:43e17289ffdbbac8f39243916c893d2ae41a2ea1a9cbb060a56a4d75286351ae"}, + {file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:781d54657063f361e89714293c095f506c533582ee40a426cb6489c48a637b81"}, + {file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6875210307d36c94873f553786a808af2788e362bd0cf4c8e66d976791e7b528"}, + {file = "orjson-3.10.15-cp38-cp38-win32.whl", hash = "sha256:305b38b2b8f8083cc3d618927d7f424349afce5975b316d33075ef0f73576b60"}, + {file = "orjson-3.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:5dd9ef1639878cc3efffed349543cbf9372bdbd79f478615a1c633fe4e4180d1"}, + {file = "orjson-3.10.15-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ffe19f3e8d68111e8644d4f4e267a069ca427926855582ff01fc012496d19969"}, + {file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d433bf32a363823863a96561a555227c18a522a8217a6f9400f00ddc70139ae2"}, + {file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da03392674f59a95d03fa5fb9fe3a160b0511ad84b7a3914699ea5a1b3a38da2"}, + {file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a63bb41559b05360ded9132032239e47983a39b151af1201f07ec9370715c82"}, + {file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3766ac4702f8f795ff3fa067968e806b4344af257011858cc3d6d8721588b53f"}, + {file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a1c73dcc8fadbd7c55802d9aa093b36878d34a3b3222c41052ce6b0fc65f8e8"}, + {file = "orjson-3.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b299383825eafe642cbab34be762ccff9fd3408d72726a6b2a4506d410a71ab3"}, + {file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:abc7abecdbf67a173ef1316036ebbf54ce400ef2300b4e26a7b843bd446c2480"}, + {file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:3614ea508d522a621384c1d6639016a5a2e4f027f3e4a1c93a51867615d28829"}, + {file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:295c70f9dc154307777ba30fe29ff15c1bcc9dfc5c48632f37d20a607e9ba85a"}, + {file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:63309e3ff924c62404923c80b9e2048c1f74ba4b615e7584584389ada50ed428"}, + {file = "orjson-3.10.15-cp39-cp39-win32.whl", hash = "sha256:a2f708c62d026fb5340788ba94a55c23df4e1869fec74be455e0b2f5363b8507"}, + {file = "orjson-3.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:efcf6c735c3d22ef60c4aa27a5238f1a477df85e9b15f2142f9d669beb2d13fd"}, + {file = "orjson-3.10.15.tar.gz", hash = "sha256:05ca7fe452a2e9d8d9d706a2984c95b9c2ebc5db417ce0b7a49b91d50642a23e"}, +] + [[package]] name = "packaging" version = "24.2" @@ -3368,7 +4488,95 @@ files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] -markers = {main = "extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\""} +markers = {main = "(extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"ranx\") and (extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\") or (extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"ranx\") and python_version >= \"3.10\""} + +[[package]] +name = "pandas" +version = "2.2.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, + {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, + {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] [[package]] name = "pandocfilters" @@ -3433,7 +4641,7 @@ description = "Python Imaging Library (Fork)" optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"sentence-transformers\"" +markers = "(python_version >= \"3.10\" or extra == \"sentence-transformers\") and (python_version == \"3.10\" or python_version == \"3.11\" or extra == \"sentence-transformers\" or extra == \"ranx\") and (extra == \"ranx\" or extra == \"sentence-transformers\")" files = [ {file = "pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8"}, {file = "pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192"}, @@ -3513,7 +4721,7 @@ docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "trove-classifiers (>=2024.10.12)"] -typing = ["typing-extensions"] +typing = ["typing-extensions ; python_version < \"3.10\""] xmp = ["defusedxml"] [[package]] @@ -3549,6 +4757,18 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "ply" +version = "3.11" +description = "Python Lex & Yacc" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"}, + {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"}, +] + [[package]] name = "pre-commit" version = "4.1.0" @@ -3585,95 +4805,111 @@ wcwidth = "*" [[package]] name = "propcache" -version = "0.2.1" +version = "0.3.0" description = "Accelerated property cache" optional = true python-versions = ">=3.9" groups = ["main"] markers = "extra == \"voyageai\"" files = [ - {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, - {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, - {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, - {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, - {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, - {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, - {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, - {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, - {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, - {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, - {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, - {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, - {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, - {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, - {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, + {file = "propcache-0.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:efa44f64c37cc30c9f05932c740a8b40ce359f51882c70883cc95feac842da4d"}, + {file = "propcache-0.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2383a17385d9800b6eb5855c2f05ee550f803878f344f58b6e194de08b96352c"}, + {file = "propcache-0.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3e7420211f5a65a54675fd860ea04173cde60a7cc20ccfbafcccd155225f8bc"}, + {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3302c5287e504d23bb0e64d2a921d1eb4a03fb93a0a0aa3b53de059f5a5d737d"}, + {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7e2e068a83552ddf7a39a99488bcba05ac13454fb205c847674da0352602082f"}, + {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d913d36bdaf368637b4f88d554fb9cb9d53d6920b9c5563846555938d5450bf"}, + {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ee1983728964d6070ab443399c476de93d5d741f71e8f6e7880a065f878e0b9"}, + {file = "propcache-0.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36ca5e9a21822cc1746023e88f5c0af6fce3af3b85d4520efb1ce4221bed75cc"}, + {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9ecde3671e62eeb99e977f5221abcf40c208f69b5eb986b061ccec317c82ebd0"}, + {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d383bf5e045d7f9d239b38e6acadd7b7fdf6c0087259a84ae3475d18e9a2ae8b"}, + {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8cb625bcb5add899cb8ba7bf716ec1d3e8f7cdea9b0713fa99eadf73b6d4986f"}, + {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5fa159dcee5dba00c1def3231c249cf261185189205073bde13797e57dd7540a"}, + {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a7080b0159ce05f179cfac592cda1a82898ca9cd097dacf8ea20ae33474fbb25"}, + {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed7161bccab7696a473fe7ddb619c1d75963732b37da4618ba12e60899fefe4f"}, + {file = "propcache-0.3.0-cp310-cp310-win32.whl", hash = "sha256:bf0d9a171908f32d54f651648c7290397b8792f4303821c42a74e7805bfb813c"}, + {file = "propcache-0.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:42924dc0c9d73e49908e35bbdec87adedd651ea24c53c29cac103ede0ea1d340"}, + {file = "propcache-0.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9ddd49258610499aab83b4f5b61b32e11fce873586282a0e972e5ab3bcadee51"}, + {file = "propcache-0.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2578541776769b500bada3f8a4eeaf944530516b6e90c089aa368266ed70c49e"}, + {file = "propcache-0.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8074c5dd61c8a3e915fa8fc04754fa55cfa5978200d2daa1e2d4294c1f136aa"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b58229a844931bca61b3a20efd2be2a2acb4ad1622fc026504309a6883686fbf"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e45377d5d6fefe1677da2a2c07b024a6dac782088e37c0b1efea4cfe2b1be19b"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec5060592d83454e8063e487696ac3783cc48c9a329498bafae0d972bc7816c9"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15010f29fbed80e711db272909a074dc79858c6d28e2915704cfc487a8ac89c6"}, + {file = "propcache-0.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a254537b9b696ede293bfdbc0a65200e8e4507bc9f37831e2a0318a9b333c85c"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2b975528998de037dfbc10144b8aed9b8dd5a99ec547f14d1cb7c5665a43f075"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:19d36bb351ad5554ff20f2ae75f88ce205b0748c38b146c75628577020351e3c"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6032231d4a5abd67c7f71168fd64a47b6b451fbcb91c8397c2f7610e67683810"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6985a593417cdbc94c7f9c3403747335e450c1599da1647a5af76539672464d3"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a1948df1bb1d56b5e7b0553c0fa04fd0e320997ae99689488201f19fa90d2e7"}, + {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8319293e85feadbbfe2150a5659dbc2ebc4afdeaf7d98936fb9a2f2ba0d4c35c"}, + {file = "propcache-0.3.0-cp311-cp311-win32.whl", hash = "sha256:63f26258a163c34542c24808f03d734b338da66ba91f410a703e505c8485791d"}, + {file = "propcache-0.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:cacea77ef7a2195f04f9279297684955e3d1ae4241092ff0cfcef532bb7a1c32"}, + {file = "propcache-0.3.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e53d19c2bf7d0d1e6998a7e693c7e87300dd971808e6618964621ccd0e01fe4e"}, + {file = "propcache-0.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a61a68d630e812b67b5bf097ab84e2cd79b48c792857dc10ba8a223f5b06a2af"}, + {file = "propcache-0.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fb91d20fa2d3b13deea98a690534697742029f4fb83673a3501ae6e3746508b5"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67054e47c01b7b349b94ed0840ccae075449503cf1fdd0a1fdd98ab5ddc2667b"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997e7b8f173a391987df40f3b52c423e5850be6f6df0dcfb5376365440b56667"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d663fd71491dde7dfdfc899d13a067a94198e90695b4321084c6e450743b8c7"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8884ba1a0fe7210b775106b25850f5e5a9dc3c840d1ae9924ee6ea2eb3acbfe7"}, + {file = "propcache-0.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa806bbc13eac1ab6291ed21ecd2dd426063ca5417dd507e6be58de20e58dfcf"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f4d7a7c0aff92e8354cceca6fe223973ddf08401047920df0fcb24be2bd5138"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9be90eebc9842a93ef8335291f57b3b7488ac24f70df96a6034a13cb58e6ff86"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bf15fc0b45914d9d1b706f7c9c4f66f2b7b053e9517e40123e137e8ca8958b3d"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a16167118677d94bb48bfcd91e420088854eb0737b76ec374b91498fb77a70e"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:41de3da5458edd5678b0f6ff66691507f9885f5fe6a0fb99a5d10d10c0fd2d64"}, + {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:728af36011bb5d344c4fe4af79cfe186729efb649d2f8b395d1572fb088a996c"}, + {file = "propcache-0.3.0-cp312-cp312-win32.whl", hash = "sha256:6b5b7fd6ee7b54e01759f2044f936dcf7dea6e7585f35490f7ca0420fe723c0d"}, + {file = "propcache-0.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:2d15bc27163cd4df433e75f546b9ac31c1ba7b0b128bfb1b90df19082466ff57"}, + {file = "propcache-0.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a2b9bf8c79b660d0ca1ad95e587818c30ccdb11f787657458d6f26a1ea18c568"}, + {file = "propcache-0.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0c1a133d42c6fc1f5fbcf5c91331657a1ff822e87989bf4a6e2e39b818d0ee9"}, + {file = "propcache-0.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bb2f144c6d98bb5cbc94adeb0447cfd4c0f991341baa68eee3f3b0c9c0e83767"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1323cd04d6e92150bcc79d0174ce347ed4b349d748b9358fd2e497b121e03c8"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b812b3cb6caacd072276ac0492d249f210006c57726b6484a1e1805b3cfeea0"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:742840d1d0438eb7ea4280f3347598f507a199a35a08294afdcc560c3739989d"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c6e7e4f9167fddc438cd653d826f2222222564daed4116a02a184b464d3ef05"}, + {file = "propcache-0.3.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a94ffc66738da99232ddffcf7910e0f69e2bbe3a0802e54426dbf0714e1c2ffe"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c6ec957025bf32b15cbc6b67afe233c65b30005e4c55fe5768e4bb518d712f1"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:549722908de62aa0b47a78b90531c022fa6e139f9166be634f667ff45632cc92"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5d62c4f6706bff5d8a52fd51fec6069bef69e7202ed481486c0bc3874912c787"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:24c04f8fbf60094c531667b8207acbae54146661657a1b1be6d3ca7773b7a545"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7c5f5290799a3f6539cc5e6f474c3e5c5fbeba74a5e1e5be75587746a940d51e"}, + {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4fa0e7c9c3cf7c276d4f6ab9af8adddc127d04e0fcabede315904d2ff76db626"}, + {file = "propcache-0.3.0-cp313-cp313-win32.whl", hash = "sha256:ee0bd3a7b2e184e88d25c9baa6a9dc609ba25b76daae942edfb14499ac7ec374"}, + {file = "propcache-0.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1c8f7d896a16da9455f882870a507567d4f58c53504dc2d4b1e1d386dfe4588a"}, + {file = "propcache-0.3.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e560fd75aaf3e5693b91bcaddd8b314f4d57e99aef8a6c6dc692f935cc1e6bbf"}, + {file = "propcache-0.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:65a37714b8ad9aba5780325228598a5b16c47ba0f8aeb3dc0514701e4413d7c0"}, + {file = "propcache-0.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:07700939b2cbd67bfb3b76a12e1412405d71019df00ca5697ce75e5ef789d829"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c0fdbdf6983526e269e5a8d53b7ae3622dd6998468821d660d0daf72779aefa"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:794c3dd744fad478b6232289c866c25406ecdfc47e294618bdf1697e69bd64a6"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4544699674faf66fb6b4473a1518ae4999c1b614f0b8297b1cef96bac25381db"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddb8870bdb83456a489ab67c6b3040a8d5a55069aa6f72f9d872235fbc52f54"}, + {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f857034dc68d5ceb30fb60afb6ff2103087aea10a01b613985610e007053a121"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:02df07041e0820cacc8f739510078f2aadcfd3fc57eaeeb16d5ded85c872c89e"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f47d52fd9b2ac418c4890aad2f6d21a6b96183c98021f0a48497a904199f006e"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9ff4e9ecb6e4b363430edf2c6e50173a63e0820e549918adef70515f87ced19a"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ecc2920630283e0783c22e2ac94427f8cca29a04cfdf331467d4f661f4072dac"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:c441c841e82c5ba7a85ad25986014be8d7849c3cfbdb6004541873505929a74e"}, + {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c929916cbdb540d3407c66f19f73387f43e7c12fa318a66f64ac99da601bcdf"}, + {file = "propcache-0.3.0-cp313-cp313t-win32.whl", hash = "sha256:0c3e893c4464ebd751b44ae76c12c5f5c1e4f6cbd6fbf67e3783cd93ad221863"}, + {file = "propcache-0.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:75e872573220d1ee2305b35c9813626e620768248425f58798413e9c39741f46"}, + {file = "propcache-0.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:03c091bb752349402f23ee43bb2bff6bd80ccab7c9df6b88ad4322258d6960fc"}, + {file = "propcache-0.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46ed02532cb66612d42ae5c3929b5e98ae330ea0f3900bc66ec5f4862069519b"}, + {file = "propcache-0.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11ae6a8a01b8a4dc79093b5d3ca2c8a4436f5ee251a9840d7790dccbd96cb649"}, + {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df03cd88f95b1b99052b52b1bb92173229d7a674df0ab06d2b25765ee8404bce"}, + {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03acd9ff19021bd0567582ac88f821b66883e158274183b9e5586f678984f8fe"}, + {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd54895e4ae7d32f1e3dd91261df46ee7483a735017dc6f987904f194aa5fd14"}, + {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26a67e5c04e3119594d8cfae517f4b9330c395df07ea65eab16f3d559b7068fe"}, + {file = "propcache-0.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee25f1ac091def37c4b59d192bbe3a206298feeb89132a470325bf76ad122a1e"}, + {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58e6d2a5a7cb3e5f166fd58e71e9a4ff504be9dc61b88167e75f835da5764d07"}, + {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:be90c94570840939fecedf99fa72839aed70b0ced449b415c85e01ae67422c90"}, + {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:49ea05212a529c2caffe411e25a59308b07d6e10bf2505d77da72891f9a05641"}, + {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:119e244ab40f70a98c91906d4c1f4c5f2e68bd0b14e7ab0a06922038fae8a20f"}, + {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:507c5357a8d8b4593b97fb669c50598f4e6cccbbf77e22fa9598aba78292b4d7"}, + {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8526b0941ec5a40220fc4dfde76aed58808e2b309c03e9fa8e2260083ef7157f"}, + {file = "propcache-0.3.0-cp39-cp39-win32.whl", hash = "sha256:7cedd25e5f678f7738da38037435b340694ab34d424938041aa630d8bac42663"}, + {file = "propcache-0.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:bf4298f366ca7e1ad1d21bbb58300a6985015909964077afd37559084590c929"}, + {file = "propcache-0.3.0-py3-none-any.whl", hash = "sha256:67dda3c7325691c2081510e92c561f465ba61b975f481735aefdfc845d2cd043"}, + {file = "propcache-0.3.0.tar.gz", hash = "sha256:a8fd93de4e1d278046345f49e2238cdb298589325849b2645d4a94c53faeffc5"}, ] [[package]] @@ -3719,33 +4955,26 @@ files = [ [[package]] name = "psutil" -version = "6.1.1" -description = "Cross-platform lib for process and system monitoring in Python." +version = "7.0.0" +description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7." optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +python-versions = ">=3.6" groups = ["dev", "docs"] files = [ - {file = "psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8"}, - {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777"}, - {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8df0178ba8a9e5bc84fed9cfa61d54601b371fbec5c8eebad27575f1e105c0d4"}, - {file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:1924e659d6c19c647e763e78670a05dbb7feaf44a0e9c94bf9e14dfc6ba50468"}, - {file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:018aeae2af92d943fdf1da6b58665124897cfc94faa2ca92098838f83e1b1bca"}, - {file = "psutil-6.1.1-cp27-none-win32.whl", hash = "sha256:6d4281f5bbca041e2292be3380ec56a9413b790579b8e593b1784499d0005dac"}, - {file = "psutil-6.1.1-cp27-none-win_amd64.whl", hash = "sha256:c777eb75bb33c47377c9af68f30e9f11bc78e0f07fbf907be4a5d70b2fe5f030"}, - {file = "psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8"}, - {file = "psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377"}, - {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003"}, - {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160"}, - {file = "psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3"}, - {file = "psutil-6.1.1-cp36-cp36m-win32.whl", hash = "sha256:384636b1a64b47814437d1173be1427a7c83681b17a450bfc309a1953e329603"}, - {file = "psutil-6.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8be07491f6ebe1a693f17d4f11e69d0dc1811fa082736500f649f79df7735303"}, - {file = "psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53"}, - {file = "psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649"}, - {file = "psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5"}, -] - -[package.extras] -dev = ["abi3audit", "black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] + {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"}, + {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993"}, + {file = "psutil-7.0.0-cp36-cp36m-win32.whl", hash = "sha256:84df4eb63e16849689f76b1ffcb36db7b8de703d1bc1fe41773db487621b6c17"}, + {file = "psutil-7.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1e744154a6580bc968a0195fd25e80432d3afec619daf145b9e5ba16cc1d688e"}, + {file = "psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99"}, + {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"}, + {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"}, +] + +[package.extras] +dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] test = ["pytest", "pytest-xdist", "setuptools"] [[package]] @@ -3837,7 +5066,7 @@ typing-extensions = ">=4.12.2" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] [[package]] name = "pydantic-core" @@ -3987,11 +5216,12 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["dev", "docs"] +groups = ["main", "dev", "docs"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, ] +markers = {main = "python_version >= \"3.10\" and extra == \"ranx\""} [package.extras] windows-terminal = ["colorama (>=0.4.6)"] @@ -4013,7 +5243,7 @@ astroid = ">=3.3.8,<=3.4.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, ] isort = ">=4.2.5,<5.13.0 || >5.13.0,<7" @@ -4027,6 +5257,22 @@ typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\"" spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] +[[package]] +name = "pyparsing" +version = "3.2.1" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1"}, + {file = "pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + [[package]] name = "pyreadline3" version = "3.5.4" @@ -4118,7 +5364,7 @@ files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] -markers = {main = "extra == \"vertexai\" or extra == \"mistralai\" or extra == \"bedrock\""} +markers = {main = "(extra == \"vertexai\" or extra == \"mistralai\" or extra == \"bedrock\" or extra == \"ranx\") and (extra == \"vertexai\" or extra == \"mistralai\" or extra == \"bedrock\") or (extra == \"vertexai\" or extra == \"mistralai\" or extra == \"bedrock\" or extra == \"ranx\") and python_version >= \"3.10\""} [package.dependencies] six = ">=1.5" @@ -4153,6 +5399,19 @@ files = [ [package.extras] pydantic = ["pydantic (>=2.0)"] +[[package]] +name = "pytz" +version = "2025.1" +description = "World timezone definitions, modern and historical" +optional = true +python-versions = "*" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, + {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, +] + [[package]] name = "pywin32" version = "308" @@ -4367,6 +5626,34 @@ files = [ [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} +[[package]] +name = "ranx" +version = "0.3.20" +description = "ranx: A Blazing-Fast Python Library for Ranking Evaluation, Comparison, and Fusion" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "ranx-0.3.20-py3-none-any.whl", hash = "sha256:e056e4d5981b0328b045868cc7064fc57a545f36009fbe9bb602295ec33335de"}, + {file = "ranx-0.3.20.tar.gz", hash = "sha256:8afc6f2042c40645e5d1fd80c35ed75a885e18bd2db7e95cc7ec32a0b41e59ea"}, +] + +[package.dependencies] +cbor2 = "*" +fastparquet = "*" +ir-datasets = "*" +lz4 = "*" +numba = ">=0.54.1" +numpy = "*" +orjson = "*" +pandas = "*" +rich = "*" +scipy = ">=1.8.0" +seaborn = "*" +tabulate = "*" +tqdm = "*" + [[package]] name = "redis" version = "5.2.1" @@ -4410,7 +5697,7 @@ description = "Alternative regular expression module, to replace re." optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"sentence-transformers\"" +markers = "extra == \"nltk\" or extra == \"sentence-transformers\"" files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -4519,7 +5806,7 @@ files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] -markers = {main = "extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"voyageai\""} +markers = {main = "(extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"voyageai\" or extra == \"ranx\") and (extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"voyageai\") or (extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"voyageai\" or extra == \"ranx\") and python_version >= \"3.10\""} [package.dependencies] certifi = ">=2017.4.17" @@ -4531,117 +5818,138 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "rich" +version = "13.9.4" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = true +python-versions = ">=3.8.0" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, + {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "rpds-py" -version = "0.22.3" +version = "0.23.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" groups = ["dev", "docs"] files = [ - {file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"}, - {file = "rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37"}, - {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24"}, - {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff"}, - {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c"}, - {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e"}, - {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec"}, - {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c"}, - {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09"}, - {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00"}, - {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf"}, - {file = "rpds_py-0.22.3-cp310-cp310-win32.whl", hash = "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652"}, - {file = "rpds_py-0.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8"}, - {file = "rpds_py-0.22.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f"}, - {file = "rpds_py-0.22.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a"}, - {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5"}, - {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb"}, - {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2"}, - {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0"}, - {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1"}, - {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d"}, - {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648"}, - {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74"}, - {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a"}, - {file = "rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64"}, - {file = "rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c"}, - {file = "rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e"}, - {file = "rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56"}, - {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45"}, - {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e"}, - {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d"}, - {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38"}, - {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15"}, - {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059"}, - {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e"}, - {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61"}, - {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7"}, - {file = "rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627"}, - {file = "rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4"}, - {file = "rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84"}, - {file = "rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25"}, - {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4"}, - {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5"}, - {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc"}, - {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b"}, - {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518"}, - {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd"}, - {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2"}, - {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16"}, - {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f"}, - {file = "rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de"}, - {file = "rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9"}, - {file = "rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b"}, - {file = "rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b"}, - {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1"}, - {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83"}, - {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd"}, - {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1"}, - {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3"}, - {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130"}, - {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c"}, - {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b"}, - {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333"}, - {file = "rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730"}, - {file = "rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf"}, - {file = "rpds_py-0.22.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea"}, - {file = "rpds_py-0.22.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e"}, - {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d"}, - {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3"}, - {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091"}, - {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e"}, - {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543"}, - {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d"}, - {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99"}, - {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831"}, - {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520"}, - {file = "rpds_py-0.22.3-cp39-cp39-win32.whl", hash = "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9"}, - {file = "rpds_py-0.22.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6"}, - {file = "rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d"}, + {file = "rpds_py-0.23.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2a54027554ce9b129fc3d633c92fa33b30de9f08bc61b32c053dc9b537266fed"}, + {file = "rpds_py-0.23.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5ef909a37e9738d146519657a1aab4584018746a18f71c692f2f22168ece40c"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ee9d6f0b38efb22ad94c3b68ffebe4c47865cdf4b17f6806d6c674e1feb4246"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f7356a6da0562190558c4fcc14f0281db191cdf4cb96e7604c06acfcee96df15"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9441af1d25aed96901f97ad83d5c3e35e6cd21a25ca5e4916c82d7dd0490a4fa"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d8abf7896a91fb97e7977d1aadfcc2c80415d6dc2f1d0fca5b8d0df247248f3"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b08027489ba8fedde72ddd233a5ea411b85a6ed78175f40285bd401bde7466d"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fee513135b5a58f3bb6d89e48326cd5aa308e4bcdf2f7d59f67c861ada482bf8"}, + {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:35d5631ce0af26318dba0ae0ac941c534453e42f569011585cb323b7774502a5"}, + {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a20cb698c4a59c534c6701b1c24a968ff2768b18ea2991f886bd8985ce17a89f"}, + {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e9c206a1abc27e0588cf8b7c8246e51f1a16a103734f7750830a1ccb63f557a"}, + {file = "rpds_py-0.23.1-cp310-cp310-win32.whl", hash = "sha256:d9f75a06ecc68f159d5d7603b734e1ff6daa9497a929150f794013aa9f6e3f12"}, + {file = "rpds_py-0.23.1-cp310-cp310-win_amd64.whl", hash = "sha256:f35eff113ad430b5272bbfc18ba111c66ff525828f24898b4e146eb479a2cdda"}, + {file = "rpds_py-0.23.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b79f5ced71efd70414a9a80bbbfaa7160da307723166f09b69773153bf17c590"}, + {file = "rpds_py-0.23.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c9e799dac1ffbe7b10c1fd42fe4cd51371a549c6e108249bde9cd1200e8f59b4"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721f9c4011b443b6e84505fc00cc7aadc9d1743f1c988e4c89353e19c4a968ee"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f88626e3f5e57432e6191cd0c5d6d6b319b635e70b40be2ffba713053e5147dd"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:285019078537949cecd0190f3690a0b0125ff743d6a53dfeb7a4e6787af154f5"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92f5654157de1379c509b15acec9d12ecf6e3bc1996571b6cb82a4302060447"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e768267cbe051dd8d1c5305ba690bb153204a09bf2e3de3ae530de955f5b5580"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c5334a71f7dc1160382d45997e29f2637c02f8a26af41073189d79b95d3321f1"}, + {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6adb81564af0cd428910f83fa7da46ce9ad47c56c0b22b50872bc4515d91966"}, + {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cafa48f2133d4daa028473ede7d81cd1b9f9e6925e9e4003ebdf77010ee02f35"}, + {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fced9fd4a07a1ded1bac7e961ddd9753dd5d8b755ba8e05acba54a21f5f1522"}, + {file = "rpds_py-0.23.1-cp311-cp311-win32.whl", hash = "sha256:243241c95174b5fb7204c04595852fe3943cc41f47aa14c3828bc18cd9d3b2d6"}, + {file = "rpds_py-0.23.1-cp311-cp311-win_amd64.whl", hash = "sha256:11dd60b2ffddba85715d8a66bb39b95ddbe389ad2cfcf42c833f1bcde0878eaf"}, + {file = "rpds_py-0.23.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3902df19540e9af4cc0c3ae75974c65d2c156b9257e91f5101a51f99136d834c"}, + {file = "rpds_py-0.23.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66f8d2a17e5838dd6fb9be6baaba8e75ae2f5fa6b6b755d597184bfcd3cb0eba"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:112b8774b0b4ee22368fec42749b94366bd9b536f8f74c3d4175d4395f5cbd31"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0df046f2266e8586cf09d00588302a32923eb6386ced0ca5c9deade6af9a149"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3288930b947cbebe767f84cf618d2cbe0b13be476e749da0e6a009f986248c"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce473a2351c018b06dd8d30d5da8ab5a0831056cc53b2006e2a8028172c37ce5"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d550d7e9e7d8676b183b37d65b5cd8de13676a738973d330b59dc8312df9c5dc"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e14f86b871ea74c3fddc9a40e947d6a5d09def5adc2076ee61fb910a9014fb35"}, + {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf5be5ba34e19be579ae873da515a2836a2166d8d7ee43be6ff909eda42b72b"}, + {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7031d493c4465dbc8d40bd6cafefef4bd472b17db0ab94c53e7909ee781b9ef"}, + {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55ff4151cfd4bc635e51cfb1c59ac9f7196b256b12e3a57deb9e5742e65941ad"}, + {file = "rpds_py-0.23.1-cp312-cp312-win32.whl", hash = "sha256:a9d3b728f5a5873d84cba997b9d617c6090ca5721caaa691f3b1a78c60adc057"}, + {file = "rpds_py-0.23.1-cp312-cp312-win_amd64.whl", hash = "sha256:b03a8d50b137ee758e4c73638b10747b7c39988eb8e6cd11abb7084266455165"}, + {file = "rpds_py-0.23.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4caafd1a22e5eaa3732acb7672a497123354bef79a9d7ceed43387d25025e935"}, + {file = "rpds_py-0.23.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:178f8a60fc24511c0eb756af741c476b87b610dba83270fce1e5a430204566a4"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c632419c3870507ca20a37c8f8f5352317aca097639e524ad129f58c125c61c6"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:698a79d295626ee292d1730bc2ef6e70a3ab135b1d79ada8fde3ed0047b65a10"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271fa2184cf28bdded86bb6217c8e08d3a169fe0bbe9be5e8d96e8476b707122"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b91cceb5add79ee563bd1f70b30896bd63bc5f78a11c1f00a1e931729ca4f1f4"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a6cb95074777f1ecda2ca4fa7717caa9ee6e534f42b7575a8f0d4cb0c24013"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:50fb62f8d8364978478b12d5f03bf028c6bc2af04082479299139dc26edf4c64"}, + {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8f7e90b948dc9dcfff8003f1ea3af08b29c062f681c05fd798e36daa3f7e3e8"}, + {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5b98b6c953e5c2bda51ab4d5b4f172617d462eebc7f4bfdc7c7e6b423f6da957"}, + {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2893d778d4671ee627bac4037a075168b2673c57186fb1a57e993465dbd79a93"}, + {file = "rpds_py-0.23.1-cp313-cp313-win32.whl", hash = "sha256:2cfa07c346a7ad07019c33fb9a63cf3acb1f5363c33bc73014e20d9fe8b01cdd"}, + {file = "rpds_py-0.23.1-cp313-cp313-win_amd64.whl", hash = "sha256:3aaf141d39f45322e44fc2c742e4b8b4098ead5317e5f884770c8df0c332da70"}, + {file = "rpds_py-0.23.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:759462b2d0aa5a04be5b3e37fb8183615f47014ae6b116e17036b131985cb731"}, + {file = "rpds_py-0.23.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3e9212f52074fc9d72cf242a84063787ab8e21e0950d4d6709886fb62bcb91d5"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e9f3a3ac919406bc0414bbbd76c6af99253c507150191ea79fab42fdb35982a"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c04ca91dda8a61584165825907f5c967ca09e9c65fe8966ee753a3f2b019fe1e"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab923167cfd945abb9b51a407407cf19f5bee35001221f2911dc85ffd35ff4f"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed6f011bedca8585787e5082cce081bac3d30f54520097b2411351b3574e1219"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6959bb9928c5c999aba4a3f5a6799d571ddc2c59ff49917ecf55be2bbb4e3722"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ed7de3c86721b4e83ac440751329ec6a1102229aa18163f84c75b06b525ad7e"}, + {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5fb89edee2fa237584e532fbf78f0ddd1e49a47c7c8cfa153ab4849dc72a35e6"}, + {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7e5413d2e2d86025e73f05510ad23dad5950ab8417b7fc6beaad99be8077138b"}, + {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d31ed4987d72aabdf521eddfb6a72988703c091cfc0064330b9e5f8d6a042ff5"}, + {file = "rpds_py-0.23.1-cp313-cp313t-win32.whl", hash = "sha256:f3429fb8e15b20961efca8c8b21432623d85db2228cc73fe22756c6637aa39e7"}, + {file = "rpds_py-0.23.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d6f6512a90bd5cd9030a6237f5346f046c6f0e40af98657568fa45695d4de59d"}, + {file = "rpds_py-0.23.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:09cd7dbcb673eb60518231e02874df66ec1296c01a4fcd733875755c02014b19"}, + {file = "rpds_py-0.23.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6760211eee3a76316cf328f5a8bd695b47b1626d21c8a27fb3b2473a884d597"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e680c1518733b73c994361e4b06441b92e973ef7d9449feec72e8ee4f713da"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae28144c1daa61366205d32abd8c90372790ff79fc60c1a8ad7fd3c8553a600e"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c698d123ce5d8f2d0cd17f73336615f6a2e3bdcedac07a1291bb4d8e7d82a05a"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98b257ae1e83f81fb947a363a274c4eb66640212516becaff7bef09a5dceacaa"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c9ff044eb07c8468594d12602291c635da292308c8c619244e30698e7fc455a"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7938c7b0599a05246d704b3f5e01be91a93b411d0d6cc62275f025293b8a11ce"}, + {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e9cb79ecedfc156c0692257ac7ed415243b6c35dd969baa461a6888fc79f2f07"}, + {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7b77e07233925bd33fc0022b8537774423e4c6680b6436316c5075e79b6384f4"}, + {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a970bfaf130c29a679b1d0a6e0f867483cea455ab1535fb427566a475078f27f"}, + {file = "rpds_py-0.23.1-cp39-cp39-win32.whl", hash = "sha256:4233df01a250b3984465faed12ad472f035b7cd5240ea3f7c76b7a7016084495"}, + {file = "rpds_py-0.23.1-cp39-cp39-win_amd64.whl", hash = "sha256:c617d7453a80e29d9973b926983b1e700a9377dbe021faa36041c78537d7b08c"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c1f8afa346ccd59e4e5630d5abb67aba6a9812fddf764fd7eb11f382a345f8cc"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fad784a31869747df4ac968a351e070c06ca377549e4ace94775aaa3ab33ee06"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5a96fcac2f18e5a0a23a75cd27ce2656c66c11c127b0318e508aab436b77428"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e77febf227a1dc3220159355dba68faa13f8dca9335d97504abf428469fb18b"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26bb3e8de93443d55e2e748e9fd87deb5f8075ca7bc0502cfc8be8687d69a2ec"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db7707dde9143a67b8812c7e66aeb2d843fe33cc8e374170f4d2c50bd8f2472d"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1eedaaccc9bb66581d4ae7c50e15856e335e57ef2734dbc5fd8ba3e2a4ab3cb6"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28358c54fffadf0ae893f6c1050e8f8853e45df22483b7fff2f6ab6152f5d8bf"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:633462ef7e61d839171bf206551d5ab42b30b71cac8f10a64a662536e057fdef"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a98f510d86f689fcb486dc59e6e363af04151e5260ad1bdddb5625c10f1e95f8"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e0397dd0b3955c61ef9b22838144aa4bef6f0796ba5cc8edfc64d468b93798b4"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:75307599f0d25bf6937248e5ac4e3bde5ea72ae6618623b86146ccc7845ed00b"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3614d280bf7aab0d3721b5ce0e73434acb90a2c993121b6e81a1c15c665298ac"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e5963ea87f88bddf7edd59644a35a0feecf75f8985430124c253612d4f7d27ae"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad76f44f70aac3a54ceb1813ca630c53415da3a24fd93c570b2dfb4856591017"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c6ae11e6e93728d86aafc51ced98b1658a0080a7dd9417d24bfb955bb09c3c2"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc869af5cba24d45fb0399b0cfdbcefcf6910bf4dee5d74036a57cf5264b3ff4"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c76b32eb2ab650a29e423525e84eb197c45504b1c1e6e17b6cc91fcfeb1a4b1d"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4263320ed887ed843f85beba67f8b2d1483b5947f2dc73a8b068924558bfeace"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7f9682a8f71acdf59fd554b82b1c12f517118ee72c0f3944eda461606dfe7eb9"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:754fba3084b70162a6b91efceee8a3f06b19e43dac3f71841662053c0584209a"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:a1c66e71ecfd2a4acf0e4bd75e7a3605afa8f9b28a3b497e4ba962719df2be57"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8d67beb6002441faef8251c45e24994de32c4c8686f7356a1f601ad7c466f7c3"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a1e17d8dc8e57d8e0fd21f8f0f0a5211b3fa258b2e444c2053471ef93fe25a00"}, + {file = "rpds_py-0.23.1.tar.gz", hash = "sha256:7f3240dcfa14d198dba24b8b9cb3b108c06b68d45b7babd9eefc1038fdf7e707"}, ] [[package]] @@ -4662,15 +5970,15 @@ pyasn1 = ">=0.1.3" [[package]] name = "s3transfer" -version = "0.11.2" +version = "0.11.3" description = "An Amazon S3 Transfer Manager" optional = true python-versions = ">=3.8" groups = ["main"] markers = "extra == \"bedrock\"" files = [ - {file = "s3transfer-0.11.2-py3-none-any.whl", hash = "sha256:be6ecb39fadd986ef1701097771f87e4d2f821f27f6071c872143884d2950fbc"}, - {file = "s3transfer-0.11.2.tar.gz", hash = "sha256:3b39185cb72f5acc77db1a58b6e25b977f28d20496b6e58d6813d75f464d632f"}, + {file = "s3transfer-0.11.3-py3-none-any.whl", hash = "sha256:ca855bdeb885174b5ffa95b9913622459d4ad8e331fc98eb01e6d5eb6a30655d"}, + {file = "s3transfer-0.11.3.tar.gz", hash = "sha256:edae4977e3a122445660c7c114bba949f9d191bae3b34a096f18a1c8c354527a"}, ] [package.dependencies] @@ -4681,28 +5989,28 @@ crt = ["botocore[crt] (>=1.36.0,<2.0a.0)"] [[package]] name = "safetensors" -version = "0.5.2" +version = "0.5.3" description = "" optional = true python-versions = ">=3.7" groups = ["main"] markers = "extra == \"sentence-transformers\"" files = [ - {file = "safetensors-0.5.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:45b6092997ceb8aa3801693781a71a99909ab9cc776fbc3fa9322d29b1d3bef2"}, - {file = "safetensors-0.5.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6d0d6a8ee2215a440e1296b843edf44fd377b055ba350eaba74655a2fe2c4bae"}, - {file = "safetensors-0.5.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86016d40bcaa3bcc9a56cd74d97e654b5f4f4abe42b038c71e4f00a089c4526c"}, - {file = "safetensors-0.5.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:990833f70a5f9c7d3fc82c94507f03179930ff7d00941c287f73b6fcbf67f19e"}, - {file = "safetensors-0.5.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dfa7c2f3fe55db34eba90c29df94bcdac4821043fc391cb5d082d9922013869"}, - {file = "safetensors-0.5.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46ff2116150ae70a4e9c490d2ab6b6e1b1b93f25e520e540abe1b81b48560c3a"}, - {file = "safetensors-0.5.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab696dfdc060caffb61dbe4066b86419107a24c804a4e373ba59be699ebd8d5"}, - {file = "safetensors-0.5.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03c937100f38c9ff4c1507abea9928a6a9b02c9c1c9c3609ed4fb2bf413d4975"}, - {file = "safetensors-0.5.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a00e737948791b94dad83cf0eafc09a02c4d8c2171a239e8c8572fe04e25960e"}, - {file = "safetensors-0.5.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:d3a06fae62418ec8e5c635b61a8086032c9e281f16c63c3af46a6efbab33156f"}, - {file = "safetensors-0.5.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1506e4c2eda1431099cebe9abf6c76853e95d0b7a95addceaa74c6019c65d8cf"}, - {file = "safetensors-0.5.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5c5b5d9da594f638a259fca766046f44c97244cc7ab8bef161b3e80d04becc76"}, - {file = "safetensors-0.5.2-cp38-abi3-win32.whl", hash = "sha256:fe55c039d97090d1f85277d402954dd6ad27f63034fa81985a9cc59655ac3ee2"}, - {file = "safetensors-0.5.2-cp38-abi3-win_amd64.whl", hash = "sha256:78abdddd03a406646107f973c7843276e7b64e5e32623529dc17f3d94a20f589"}, - {file = "safetensors-0.5.2.tar.gz", hash = "sha256:cb4a8d98ba12fa016f4241932b1fc5e702e5143f5374bba0bbcf7ddc1c4cf2b8"}, + {file = "safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073"}, + {file = "safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7"}, + {file = "safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467"}, + {file = "safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e"}, + {file = "safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d"}, + {file = "safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9"}, + {file = "safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a"}, + {file = "safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d"}, + {file = "safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b"}, + {file = "safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff"}, + {file = "safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135"}, + {file = "safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04"}, + {file = "safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace"}, + {file = "safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11"}, + {file = "safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965"}, ] [package.extras] @@ -4781,7 +6089,7 @@ description = "Fundamental algorithms for scientific computing in Python" optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"sentence-transformers\"" +markers = "python_version < \"3.10\" and extra == \"sentence-transformers\"" files = [ {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, @@ -4820,53 +6128,59 @@ test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "po [[package]] name = "scipy" -version = "1.15.1" +version = "1.15.2" description = "Fundamental algorithms for scientific computing in Python" optional = true python-versions = ">=3.10" groups = ["main"] -markers = "extra == \"sentence-transformers\" and python_version >= \"3.10\"" -files = [ - {file = "scipy-1.15.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:c64ded12dcab08afff9e805a67ff4480f5e69993310e093434b10e85dc9d43e1"}, - {file = "scipy-1.15.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5b190b935e7db569960b48840e5bef71dc513314cc4e79a1b7d14664f57fd4ff"}, - {file = "scipy-1.15.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:4b17d4220df99bacb63065c76b0d1126d82bbf00167d1730019d2a30d6ae01ea"}, - {file = "scipy-1.15.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:63b9b6cd0333d0eb1a49de6f834e8aeaefe438df8f6372352084535ad095219e"}, - {file = "scipy-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f151e9fb60fbf8e52426132f473221a49362091ce7a5e72f8aa41f8e0da4f25"}, - {file = "scipy-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21e10b1dd56ce92fba3e786007322542361984f8463c6d37f6f25935a5a6ef52"}, - {file = "scipy-1.15.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5dff14e75cdbcf07cdaa1c7707db6017d130f0af9ac41f6ce443a93318d6c6e0"}, - {file = "scipy-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:f82fcf4e5b377f819542fbc8541f7b5fbcf1c0017d0df0bc22c781bf60abc4d8"}, - {file = "scipy-1.15.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:5bd8d27d44e2c13d0c1124e6a556454f52cd3f704742985f6b09e75e163d20d2"}, - {file = "scipy-1.15.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:be3deeb32844c27599347faa077b359584ba96664c5c79d71a354b80a0ad0ce0"}, - {file = "scipy-1.15.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:5eb0ca35d4b08e95da99a9f9c400dc9f6c21c424298a0ba876fdc69c7afacedf"}, - {file = "scipy-1.15.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:74bb864ff7640dea310a1377d8567dc2cb7599c26a79ca852fc184cc851954ac"}, - {file = "scipy-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:667f950bf8b7c3a23b4199db24cb9bf7512e27e86d0e3813f015b74ec2c6e3df"}, - {file = "scipy-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395be70220d1189756068b3173853029a013d8c8dd5fd3d1361d505b2aa58fa7"}, - {file = "scipy-1.15.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ce3a000cd28b4430426db2ca44d96636f701ed12e2b3ca1f2b1dd7abdd84b39a"}, - {file = "scipy-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:3fe1d95944f9cf6ba77aa28b82dd6bb2a5b52f2026beb39ecf05304b8392864b"}, - {file = "scipy-1.15.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c09aa9d90f3500ea4c9b393ee96f96b0ccb27f2f350d09a47f533293c78ea776"}, - {file = "scipy-1.15.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:0ac102ce99934b162914b1e4a6b94ca7da0f4058b6d6fd65b0cef330c0f3346f"}, - {file = "scipy-1.15.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:09c52320c42d7f5c7748b69e9f0389266fd4f82cf34c38485c14ee976cb8cb04"}, - {file = "scipy-1.15.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:cdde8414154054763b42b74fe8ce89d7f3d17a7ac5dd77204f0e142cdc9239e9"}, - {file = "scipy-1.15.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c9d8fc81d6a3b6844235e6fd175ee1d4c060163905a2becce8e74cb0d7554ce"}, - {file = "scipy-1.15.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fb57b30f0017d4afa5fe5f5b150b8f807618819287c21cbe51130de7ccdaed2"}, - {file = "scipy-1.15.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:491d57fe89927fa1aafbe260f4cfa5ffa20ab9f1435025045a5315006a91b8f5"}, - {file = "scipy-1.15.1-cp312-cp312-win_amd64.whl", hash = "sha256:900f3fa3db87257510f011c292a5779eb627043dd89731b9c461cd16ef76ab3d"}, - {file = "scipy-1.15.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:100193bb72fbff37dbd0bf14322314fc7cbe08b7ff3137f11a34d06dc0ee6b85"}, - {file = "scipy-1.15.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:2114a08daec64980e4b4cbdf5bee90935af66d750146b1d2feb0d3ac30613692"}, - {file = "scipy-1.15.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6b3e71893c6687fc5e29208d518900c24ea372a862854c9888368c0b267387ab"}, - {file = "scipy-1.15.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:837299eec3d19b7e042923448d17d95a86e43941104d33f00da7e31a0f715d3c"}, - {file = "scipy-1.15.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82add84e8a9fb12af5c2c1a3a3f1cb51849d27a580cb9e6bd66226195142be6e"}, - {file = "scipy-1.15.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:070d10654f0cb6abd295bc96c12656f948e623ec5f9a4eab0ddb1466c000716e"}, - {file = "scipy-1.15.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:55cc79ce4085c702ac31e49b1e69b27ef41111f22beafb9b49fea67142b696c4"}, - {file = "scipy-1.15.1-cp313-cp313-win_amd64.whl", hash = "sha256:c352c1b6d7cac452534517e022f8f7b8d139cd9f27e6fbd9f3cbd0bfd39f5bef"}, - {file = "scipy-1.15.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0458839c9f873062db69a03de9a9765ae2e694352c76a16be44f93ea45c28d2b"}, - {file = "scipy-1.15.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:af0b61c1de46d0565b4b39c6417373304c1d4f5220004058bdad3061c9fa8a95"}, - {file = "scipy-1.15.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:71ba9a76c2390eca6e359be81a3e879614af3a71dfdabb96d1d7ab33da6f2364"}, - {file = "scipy-1.15.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14eaa373c89eaf553be73c3affb11ec6c37493b7eaaf31cf9ac5dffae700c2e0"}, - {file = "scipy-1.15.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f735bc41bd1c792c96bc426dece66c8723283695f02df61dcc4d0a707a42fc54"}, - {file = "scipy-1.15.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2722a021a7929d21168830790202a75dbb20b468a8133c74a2c0230c72626b6c"}, - {file = "scipy-1.15.1-cp313-cp313t-win_amd64.whl", hash = "sha256:bc7136626261ac1ed988dca56cfc4ab5180f75e0ee52e58f1e6aa74b5f3eacd5"}, - {file = "scipy-1.15.1.tar.gz", hash = "sha256:033a75ddad1463970c96a88063a1df87ccfddd526437136b6ee81ff0312ebdf6"}, +markers = "python_version >= \"3.10\" and (extra == \"ranx\" or extra == \"sentence-transformers\")" +files = [ + {file = "scipy-1.15.2-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a2ec871edaa863e8213ea5df811cd600734f6400b4af272e1c011e69401218e9"}, + {file = "scipy-1.15.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:6f223753c6ea76983af380787611ae1291e3ceb23917393079dcc746ba60cfb5"}, + {file = "scipy-1.15.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:ecf797d2d798cf7c838c6d98321061eb3e72a74710e6c40540f0e8087e3b499e"}, + {file = "scipy-1.15.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:9b18aa747da280664642997e65aab1dd19d0c3d17068a04b3fe34e2559196cb9"}, + {file = "scipy-1.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87994da02e73549dfecaed9e09a4f9d58a045a053865679aeb8d6d43747d4df3"}, + {file = "scipy-1.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69ea6e56d00977f355c0f84eba69877b6df084516c602d93a33812aa04d90a3d"}, + {file = "scipy-1.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:888307125ea0c4466287191e5606a2c910963405ce9671448ff9c81c53f85f58"}, + {file = "scipy-1.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9412f5e408b397ff5641080ed1e798623dbe1ec0d78e72c9eca8992976fa65aa"}, + {file = "scipy-1.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:b5e025e903b4f166ea03b109bb241355b9c42c279ea694d8864d033727205e65"}, + {file = "scipy-1.15.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:92233b2df6938147be6fa8824b8136f29a18f016ecde986666be5f4d686a91a4"}, + {file = "scipy-1.15.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:62ca1ff3eb513e09ed17a5736929429189adf16d2d740f44e53270cc800ecff1"}, + {file = "scipy-1.15.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4c6676490ad76d1c2894d77f976144b41bd1a4052107902238047fb6a473e971"}, + {file = "scipy-1.15.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8bf5cb4a25046ac61d38f8d3c3426ec11ebc350246a4642f2f315fe95bda655"}, + {file = "scipy-1.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a8e34cf4c188b6dd004654f88586d78f95639e48a25dfae9c5e34a6dc34547e"}, + {file = "scipy-1.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28a0d2c2075946346e4408b211240764759e0fabaeb08d871639b5f3b1aca8a0"}, + {file = "scipy-1.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:42dabaaa798e987c425ed76062794e93a243be8f0f20fff6e7a89f4d61cb3d40"}, + {file = "scipy-1.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f5e296ec63c5da6ba6fa0343ea73fd51b8b3e1a300b0a8cae3ed4b1122c7462"}, + {file = "scipy-1.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:597a0c7008b21c035831c39927406c6181bcf8f60a73f36219b69d010aa04737"}, + {file = "scipy-1.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c4697a10da8f8765bb7c83e24a470da5797e37041edfd77fd95ba3811a47c4fd"}, + {file = "scipy-1.15.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:869269b767d5ee7ea6991ed7e22b3ca1f22de73ab9a49c44bad338b725603301"}, + {file = "scipy-1.15.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:bad78d580270a4d32470563ea86c6590b465cb98f83d760ff5b0990cb5518a93"}, + {file = "scipy-1.15.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b09ae80010f52efddb15551025f9016c910296cf70adbf03ce2a8704f3a5ad20"}, + {file = "scipy-1.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a6fd6eac1ce74a9f77a7fc724080d507c5812d61e72bd5e4c489b042455865e"}, + {file = "scipy-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b871df1fe1a3ba85d90e22742b93584f8d2b8e6124f8372ab15c71b73e428b8"}, + {file = "scipy-1.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:03205d57a28e18dfd39f0377d5002725bf1f19a46f444108c29bdb246b6c8a11"}, + {file = "scipy-1.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:601881dfb761311045b03114c5fe718a12634e5608c3b403737ae463c9885d53"}, + {file = "scipy-1.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:e7c68b6a43259ba0aab737237876e5c2c549a031ddb7abc28c7b47f22e202ded"}, + {file = "scipy-1.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01edfac9f0798ad6b46d9c4c9ca0e0ad23dbf0b1eb70e96adb9fa7f525eff0bf"}, + {file = "scipy-1.15.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:08b57a9336b8e79b305a143c3655cc5bdbe6d5ece3378578888d2afbb51c4e37"}, + {file = "scipy-1.15.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:54c462098484e7466362a9f1672d20888f724911a74c22ae35b61f9c5919183d"}, + {file = "scipy-1.15.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:cf72ff559a53a6a6d77bd8eefd12a17995ffa44ad86c77a5df96f533d4e6c6bb"}, + {file = "scipy-1.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9de9d1416b3d9e7df9923ab23cd2fe714244af10b763975bea9e4f2e81cebd27"}, + {file = "scipy-1.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb530e4794fc8ea76a4a21ccb67dea33e5e0e60f07fc38a49e821e1eae3b71a0"}, + {file = "scipy-1.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5ea7ed46d437fc52350b028b1d44e002646e28f3e8ddc714011aaf87330f2f32"}, + {file = "scipy-1.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11e7ad32cf184b74380f43d3c0a706f49358b904fa7d5345f16ddf993609184d"}, + {file = "scipy-1.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:a5080a79dfb9b78b768cebf3c9dcbc7b665c5875793569f48bf0e2b1d7f68f6f"}, + {file = "scipy-1.15.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:447ce30cee6a9d5d1379087c9e474628dab3db4a67484be1b7dc3196bfb2fac9"}, + {file = "scipy-1.15.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c90ebe8aaa4397eaefa8455a8182b164a6cc1d59ad53f79943f266d99f68687f"}, + {file = "scipy-1.15.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:def751dd08243934c884a3221156d63e15234a3155cf25978b0a668409d45eb6"}, + {file = "scipy-1.15.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:302093e7dfb120e55515936cb55618ee0b895f8bcaf18ff81eca086c17bd80af"}, + {file = "scipy-1.15.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd5b77413e1855351cdde594eca99c1f4a588c2d63711388b6a1f1c01f62274"}, + {file = "scipy-1.15.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d0194c37037707b2afa7a2f2a924cf7bac3dc292d51b6a925e5fcb89bc5c776"}, + {file = "scipy-1.15.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:bae43364d600fdc3ac327db99659dcb79e6e7ecd279a75fe1266669d9a652828"}, + {file = "scipy-1.15.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f031846580d9acccd0044efd1a90e6f4df3a6e12b4b6bd694a7bc03a89892b28"}, + {file = "scipy-1.15.2-cp313-cp313t-win_amd64.whl", hash = "sha256:fe8a9eb875d430d81755472c5ba75e84acc980e4a8f6204d402849234d3017db"}, + {file = "scipy-1.15.2.tar.gz", hash = "sha256:cd58a314d92838f7e6f755c8a2167ead4f27e1fd5c1251fd54289569ef3495ec"}, ] [package.dependencies] @@ -4875,7 +6189,30 @@ numpy = ">=1.23.5,<2.5" [package.extras] dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.16.5)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"] -test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja ; sys_platform != \"emscripten\"", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "seaborn" +version = "0.13.2" +description = "Statistical data visualization" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987"}, + {file = "seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7"}, +] + +[package.dependencies] +matplotlib = ">=3.4,<3.6.1 || >3.6.1" +numpy = ">=1.20,<1.24.0 || >1.24.0" +pandas = ">=1.2" + +[package.extras] +dev = ["flake8", "flit", "mypy", "pandas-stubs", "pre-commit", "pytest", "pytest-cov", "pytest-xdist"] +docs = ["ipykernel", "nbconvert", "numpydoc", "pydata_sphinx_theme (==0.10.0rc2)", "pyyaml", "sphinx (<6.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-issues"] +stats = ["scipy (>=1.7)", "statsmodels (>=0.12)"] [[package]] name = "sentence-transformers" @@ -4908,25 +6245,25 @@ train = ["accelerate (>=0.20.3)", "datasets"] [[package]] name = "setuptools" -version = "75.8.0" +version = "75.8.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"sentence-transformers\" and python_version >= \"3.12\"" +markers = "python_version >= \"3.12\" and extra == \"sentence-transformers\"" files = [ - {file = "setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3"}, - {file = "setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6"}, + {file = "setuptools-75.8.2-py3-none-any.whl", hash = "sha256:558e47c15f1811c1fa7adbd0096669bf76c1d3f433f58324df69f3f5ecac4e8f"}, + {file = "setuptools-75.8.2.tar.gz", hash = "sha256:4880473a969e5f23f2a2be3646b2dfd84af9028716d398e46192f84bc36900d2"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] -core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "shapely" @@ -4999,7 +6336,7 @@ files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] -markers = {main = "extra == \"vertexai\" or extra == \"mistralai\" or extra == \"bedrock\""} +markers = {main = "(extra == \"vertexai\" or extra == \"mistralai\" or extra == \"bedrock\" or extra == \"ranx\") and (extra == \"vertexai\" or extra == \"mistralai\" or extra == \"bedrock\") or (extra == \"vertexai\" or extra == \"mistralai\" or extra == \"bedrock\" or extra == \"ranx\") and python_version >= \"3.10\""} [[package]] name = "sniffio" @@ -5032,11 +6369,12 @@ version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" -groups = ["docs"] +groups = ["main", "docs"] files = [ {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] +markers = {main = "python_version >= \"3.10\" and extra == \"ranx\""} [[package]] name = "sphinx" @@ -5278,16 +6616,27 @@ files = [ {file = "SQLAlchemy-2.0.38-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5dba1cdb8f319084f5b00d41207b2079822aa8d6a4667c0f369fce85e34b0c86"}, {file = "SQLAlchemy-2.0.38-cp313-cp313-win32.whl", hash = "sha256:eae27ad7580529a427cfdd52c87abb2dfb15ce2b7a3e0fc29fbb63e2ed6f8120"}, {file = "SQLAlchemy-2.0.38-cp313-cp313-win_amd64.whl", hash = "sha256:b335a7c958bc945e10c522c069cd6e5804f4ff20f9a744dd38e748eb602cbbda"}, + {file = "SQLAlchemy-2.0.38-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:40310db77a55512a18827488e592965d3dec6a3f1e3d8af3f8243134029daca3"}, {file = "SQLAlchemy-2.0.38-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d3043375dd5bbcb2282894cbb12e6c559654c67b5fffb462fda815a55bf93f7"}, + {file = "SQLAlchemy-2.0.38-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70065dfabf023b155a9c2a18f573e47e6ca709b9e8619b2e04c54d5bcf193178"}, {file = "SQLAlchemy-2.0.38-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:c058b84c3b24812c859300f3b5abf300daa34df20d4d4f42e9652a4d1c48c8a4"}, + {file = "SQLAlchemy-2.0.38-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0398361acebb42975deb747a824b5188817d32b5c8f8aba767d51ad0cc7bb08d"}, {file = "SQLAlchemy-2.0.38-cp37-cp37m-win32.whl", hash = "sha256:a2bc4e49e8329f3283d99840c136ff2cd1a29e49b5624a46a290f04dff48e079"}, {file = "SQLAlchemy-2.0.38-cp37-cp37m-win_amd64.whl", hash = "sha256:9cd136184dd5f58892f24001cdce986f5d7e96059d004118d5410671579834a4"}, + {file = "SQLAlchemy-2.0.38-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:665255e7aae5f38237b3a6eae49d2358d83a59f39ac21036413fab5d1e810578"}, + {file = "SQLAlchemy-2.0.38-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:92f99f2623ff16bd4aaf786ccde759c1f676d39c7bf2855eb0b540e1ac4530c8"}, {file = "SQLAlchemy-2.0.38-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa498d1392216fae47eaf10c593e06c34476ced9549657fca713d0d1ba5f7248"}, + {file = "SQLAlchemy-2.0.38-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9afbc3909d0274d6ac8ec891e30210563b2c8bdd52ebbda14146354e7a69373"}, {file = "SQLAlchemy-2.0.38-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:57dd41ba32430cbcc812041d4de8d2ca4651aeefad2626921ae2a23deb8cd6ff"}, + {file = "SQLAlchemy-2.0.38-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3e35d5565b35b66905b79ca4ae85840a8d40d31e0b3e2990f2e7692071b179ca"}, {file = "SQLAlchemy-2.0.38-cp38-cp38-win32.whl", hash = "sha256:f0d3de936b192980209d7b5149e3c98977c3810d401482d05fb6d668d53c1c63"}, {file = "SQLAlchemy-2.0.38-cp38-cp38-win_amd64.whl", hash = "sha256:3868acb639c136d98107c9096303d2d8e5da2880f7706f9f8c06a7f961961149"}, + {file = "SQLAlchemy-2.0.38-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07258341402a718f166618470cde0c34e4cec85a39767dce4e24f61ba5e667ea"}, + {file = "SQLAlchemy-2.0.38-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a826f21848632add58bef4f755a33d45105d25656a0c849f2dc2df1c71f6f50"}, {file = "SQLAlchemy-2.0.38-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:386b7d136919bb66ced64d2228b92d66140de5fefb3c7df6bd79069a269a7b06"}, + {file = "SQLAlchemy-2.0.38-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f2951dc4b4f990a4b394d6b382accb33141d4d3bd3ef4e2b27287135d6bdd68"}, {file = "SQLAlchemy-2.0.38-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8bf312ed8ac096d674c6aa9131b249093c1b37c35db6a967daa4c84746bc1bc9"}, + {file = "SQLAlchemy-2.0.38-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6db316d6e340f862ec059dc12e395d71f39746a20503b124edc255973977b728"}, {file = "SQLAlchemy-2.0.38-cp39-cp39-win32.whl", hash = "sha256:c09a6ea87658695e527104cf857c70f79f14e9484605e205217aae0ec27b45fc"}, {file = "SQLAlchemy-2.0.38-cp39-cp39-win_amd64.whl", hash = "sha256:12f5c9ed53334c3ce719155424dc5407aaa4f6cadeb09c5b627e06abb93933a1"}, {file = "SQLAlchemy-2.0.38-py3-none-any.whl", hash = "sha256:63178c675d4c80def39f1febd625a6333f44c0ba269edd8a468b156394b27753"}, @@ -5520,7 +6869,7 @@ description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev", "docs"] -markers = "python_version < \"3.11\"" +markers = "python_version <= \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -5654,7 +7003,7 @@ description = "Fast, Extensible Progress Meter" optional = true python-versions = ">=3.7" groups = ["main"] -markers = "extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"openai\"" +markers = "(extra == \"nltk\" or extra == \"openai\" or extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"ranx\") and (extra == \"nltk\" or extra == \"openai\" or extra == \"sentence-transformers\" or extra == \"cohere\") or (extra == \"nltk\" or extra == \"openai\" or extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"ranx\") and python_version >= \"3.10\"" files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -5688,20 +7037,20 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "transformers" -version = "4.48.3" +version = "4.49.0" description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" optional = true python-versions = ">=3.9.0" groups = ["main"] markers = "extra == \"sentence-transformers\"" files = [ - {file = "transformers-4.48.3-py3-none-any.whl", hash = "sha256:78697f990f5ef350c23b46bf86d5081ce96b49479ab180b2de7687267de8fd36"}, - {file = "transformers-4.48.3.tar.gz", hash = "sha256:a5e8f1e9a6430aa78215836be70cecd3f872d99eeda300f41ad6cc841724afdb"}, + {file = "transformers-4.49.0-py3-none-any.whl", hash = "sha256:6b4fded1c5fee04d384b1014495b4235a2b53c87503d7d592423c06128cbbe03"}, + {file = "transformers-4.49.0.tar.gz", hash = "sha256:7e40e640b5b8dc3f48743f5f5adbdce3660c82baafbd3afdfc04143cdbd2089e"}, ] [package.dependencies] filelock = "*" -huggingface-hub = ">=0.24.0,<1.0" +huggingface-hub = ">=0.26.0,<1.0" numpy = ">=1.17" packaging = ">=20.0" pyyaml = ">=5.1" @@ -5714,13 +7063,13 @@ tqdm = ">=4.27" [package.extras] accelerate = ["accelerate (>=0.26.0)"] agents = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "datasets (!=2.5.0)", "diffusers", "opencv-python", "sentencepiece (>=0.1.91,!=0.1.92)", "torch (>=2.0)"] -all = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av (==9.2.0)", "codecarbon (>=2.8.1)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch (>=2.0)", "torchaudio", "torchvision"] +all = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av", "codecarbon (>=2.8.1)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch (>=2.0)", "torchaudio", "torchvision"] audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] benchmark = ["optimum-benchmark (>=0.3.0)"] codecarbon = ["codecarbon (>=2.8.1)"] deepspeed = ["accelerate (>=0.26.0)", "deepspeed (>=0.9.3)"] deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk (<=3.8.1)", "optuna", "parameterized", "protobuf", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-asyncio", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] -dev = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av (==9.2.0)", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-asyncio", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch (>=2.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +dev = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "av", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-asyncio", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch (>=2.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] dev-tensorflow = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "librosa", "nltk (<=3.8.1)", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-asyncio", "pytest-rich", "pytest-timeout", "pytest-xdist", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.21,<0.22)", "urllib3 (<2.0.0)"] dev-torch = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "libcst", "librosa", "nltk (<=3.8.1)", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-asyncio", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.5.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (<=1.0.11)", "tokenizers (>=0.21,<0.22)", "torch (>=2.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)", "scipy (<1.13.0)"] @@ -5753,10 +7102,27 @@ tokenizers = ["tokenizers (>=0.21,<0.22)"] torch = ["accelerate (>=0.26.0)", "torch (>=2.0)"] torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] torch-vision = ["Pillow (>=10.0.1,<=15.0)", "torchvision"] -torchhub = ["filelock", "huggingface-hub (>=0.24.0,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.21,<0.22)", "torch (>=2.0)", "tqdm (>=4.27)"] -video = ["av (==9.2.0)"] +torchhub = ["filelock", "huggingface-hub (>=0.26.0,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.21,<0.22)", "torch (>=2.0)", "tqdm (>=4.27)"] +video = ["av"] vision = ["Pillow (>=10.0.1,<=15.0)"] +[[package]] +name = "trec-car-tools" +version = "2.6" +description = "Support tools for TREC CAR participants. Also see trec-car.cs.unh.edu" +optional = true +python-versions = ">=3.6" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "trec-car-tools-2.6.tar.gz", hash = "sha256:2fce2de120224fd569b151d5bed358a4ed334e643889b9e3dfe3e5a3d15d21c8"}, + {file = "trec_car_tools-2.6-py3-none-any.whl", hash = "sha256:e6f0373259e1c234222da7270ab54ca7af7a6f8d0dd32b13e158c1659d3991cf"}, +] + +[package.dependencies] +cbor = ">=1.0.0" +numpy = ">=1.11.2" + [[package]] name = "triton" version = "3.2.0" @@ -5860,7 +7226,7 @@ description = "Typing stubs for requests" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"cohere\" and python_version >= \"3.10\"" +markers = "python_version >= \"3.10\" and extra == \"cohere\"" files = [ {file = "types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95"}, {file = "types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747"}, @@ -5871,14 +7237,14 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "75.8.0.20250210" +version = "75.8.0.20250225" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "types_setuptools-75.8.0.20250210-py3-none-any.whl", hash = "sha256:a217d7b4d59be04c29e23d142c959a0f85e71292fd3fc4313f016ca11f0b56dc"}, - {file = "types_setuptools-75.8.0.20250210.tar.gz", hash = "sha256:c1547361b2441f07c94e25dce8a068e18c611593ad4b6fdd727b1a8f5d1fda33"}, + {file = "types_setuptools-75.8.0.20250225-py3-none-any.whl", hash = "sha256:94c86b439cc60bcc68c1cda3fd2c301f007f8f9502f4fbb54c66cb5ce9b875af"}, + {file = "types_setuptools-75.8.0.20250225.tar.gz", hash = "sha256:6038f7e983d55792a5f90d8fdbf5d4c186026214a16bb65dd6ae83c624ae9636"}, ] [[package]] @@ -5935,6 +7301,36 @@ files = [ mypy-extensions = ">=0.3.0" typing-extensions = ">=3.7.4" +[[package]] +name = "tzdata" +version = "2025.1" +description = "Provider of IANA time zone data" +optional = true +python-versions = ">=2" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"}, + {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"}, +] + +[[package]] +name = "unlzw3" +version = "0.2.3" +description = "Pure Python decompression module for .Z files compressed using Unix compress utility" +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "unlzw3-0.2.3-py3-none-any.whl", hash = "sha256:7760fb4f3afa1225623944c061991d89a061f7fb78665dbc4cddfdb562bb4a8b"}, + {file = "unlzw3-0.2.3.tar.gz", hash = "sha256:ede5d928c792fff9da406f20334f9739693327f448f383ae1df1774627197bbb"}, +] + +[package.extras] +lint = ["flake8", "flake8-blind-except", "flake8-bugbear", "flake8-builtins", "mypy"] +tests = ["pytest"] + [[package]] name = "urllib3" version = "1.26.20" @@ -5946,11 +7342,11 @@ files = [ {file = "urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e"}, {file = "urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32"}, ] -markers = {main = "extra == \"sentence-transformers\" and python_version < \"3.10\" or extra == \"cohere\" and python_version < \"3.10\" or extra == \"vertexai\" and python_version < \"3.10\" or extra == \"voyageai\" and python_version < \"3.10\" or extra == \"bedrock\" and python_version < \"3.10\"", dev = "python_version < \"3.10\"", docs = "python_version < \"3.10\""} +markers = {main = "python_version < \"3.10\" and (extra == \"bedrock\" or extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"voyageai\")", dev = "python_version < \"3.10\"", docs = "python_version < \"3.10\""} [package.extras] -brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +brotli = ["brotli (==1.0.9) ; os_name != \"nt\" and python_version < \"3\" and platform_python_implementation == \"CPython\"", "brotli (>=1.0.9) ; python_version >= \"3\" and platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; (os_name != \"nt\" or python_version >= \"3\") and platform_python_implementation != \"CPython\"", "brotlipy (>=0.6.0) ; os_name == \"nt\" and python_version < \"3\""] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress ; python_version == \"2.7\"", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] @@ -5964,10 +7360,10 @@ files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] -markers = {main = "(extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"voyageai\" or extra == \"bedrock\") and python_version >= \"3.10\"", dev = "python_version >= \"3.10\"", docs = "python_version >= \"3.10\""} +markers = {main = "python_version >= \"3.10\" and (extra == \"ranx\" or extra == \"bedrock\" or extra == \"sentence-transformers\" or extra == \"cohere\" or extra == \"vertexai\" or extra == \"voyageai\")", dev = "python_version >= \"3.10\"", docs = "python_version >= \"3.10\""} [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -5991,7 +7387,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "voyageai" @@ -6013,6 +7409,31 @@ numpy = ">=1.11" requests = ">=2.20,<3.0" tenacity = ">=8.0.1" +[[package]] +name = "warc3-wet" +version = "0.2.5" +description = "Python library to work with ARC and WARC files" +optional = true +python-versions = "*" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "warc3_wet-0.2.5-py3-none-any.whl", hash = "sha256:5a9a525383fb1af159734baa75f349a7c4ec7bccd1b938681b5748515d2bf624"}, + {file = "warc3_wet-0.2.5.tar.gz", hash = "sha256:15e50402dabaa1e95307f1e2a6169cfd5f137b70761d9f0b16a10aa6de227970"}, +] + +[[package]] +name = "warc3-wet-clueweb09" +version = "0.2.5" +description = "Python library to work with ARC and WARC files, with fixes for ClueWeb09" +optional = true +python-versions = "*" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "warc3-wet-clueweb09-0.2.5.tar.gz", hash = "sha256:3054bfc07da525d5967df8ca3175f78fa3f78514c82643f8c81fbca96300b836"}, +] + [[package]] name = "wcwidth" version = "0.2.13" @@ -6238,18 +7659,59 @@ files = [ markers = {dev = "python_version < \"3.10\""} [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] +[[package]] +name = "zlib-state" +version = "0.1.9" +description = "Low-level interface to the zlib library that enables capturing the decoding state" +optional = true +python-versions = ">=3.6" +groups = ["main"] +markers = "python_version >= \"3.10\" and extra == \"ranx\"" +files = [ + {file = "zlib_state-0.1.9-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97f45d0f80e9d7070229ecb36112eea6a17dc40053449a9c613ef837d9cb66b4"}, + {file = "zlib_state-0.1.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3564eaa130f2533b87b82d0e622cfb5c25acec123e7bfe38d39db9ce6349cb52"}, + {file = "zlib_state-0.1.9-cp310-cp310-win_amd64.whl", hash = "sha256:0e633bd3fb65cd8c8f0fc5870cdd40354f218f815cc7a53fb525410251f06ab9"}, + {file = "zlib_state-0.1.9-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bbfd191c908be2ba04319e57b4d166f9c625204c1a8c85d2eb968d9d7d14dcb"}, + {file = "zlib_state-0.1.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66dd680ef5c0d21fe1e673a4c68173feeda20f7933e3468c22c44d5960ebf621"}, + {file = "zlib_state-0.1.9-cp311-cp311-win_amd64.whl", hash = "sha256:cdb7cdf2515d8c70c6a99a331bf8c1486b3bda77371e951961272cc9888494e1"}, + {file = "zlib_state-0.1.9-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22bc6ea28d1cbb717e7ba8254b12da5cff0820309d7ff46dba083d2dc44fd69"}, + {file = "zlib_state-0.1.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06ed845442af6fc8ad885037b1393c02ff1554638cd43ff8718ca1fb8999b7c7"}, + {file = "zlib_state-0.1.9-cp312-cp312-win_amd64.whl", hash = "sha256:862b120477db67df4ad8af8c135fe134ae4051693d6a6abf1c208d9d1170d7d8"}, + {file = "zlib_state-0.1.9-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8686b95a3510066aea346b3baf4067f23488d1b4832b48ccdb4863e1968b00bd"}, + {file = "zlib_state-0.1.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfc20e1be15d7a29e3b29c12860727f5eed94dd3e5d55f087a27051b5c0a0d59"}, + {file = "zlib_state-0.1.9-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e96af2f329e0931e96030a7294b52821aecfa390fee8c12dc2d0a143b1dd2720"}, + {file = "zlib_state-0.1.9-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2a0dec8c12a7df84435d3777f88c6fb424e5405c7151a3c219f18257f7830b7"}, + {file = "zlib_state-0.1.9-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2557c04013733022b14023edbf9a52000ecf5dfee530b925abe33e9eb2167ac"}, + {file = "zlib_state-0.1.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83d246e7c56eca91b0dfa6757617b452c80af9dfc1c26c22cae83972dc3659e1"}, + {file = "zlib_state-0.1.9-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33d731a7cdeb310dbe8ae7473ae5b06b7d905c6459791356de884944f734077f"}, + {file = "zlib_state-0.1.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4ceef43fde93b2626655b22a8c88768607e844ea3d2ca6429d79beeeda1a671"}, + {file = "zlib_state-0.1.9-cp38-cp38-win_amd64.whl", hash = "sha256:45fcd1c322daac34785250a7b1d099402e62eb54e9318c952de6b527737d2776"}, + {file = "zlib_state-0.1.9-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:001b5fb0af67e978e6d50b9ac8a1aed9d2411d8131c032eec3f02d65f23fb5a0"}, + {file = "zlib_state-0.1.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8255a96bc6ff5964cc665e718ea1c8da4895bdc292536a32d11396d63524609"}, + {file = "zlib_state-0.1.9-cp39-cp39-win_amd64.whl", hash = "sha256:20811b7271c721baac2feb3f2b14aeda66e4989d6df2edc3242bab3a820e2279"}, + {file = "zlib_state-0.1.9-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc968a30b50a5cc6c0e45a6e756f370b6530445247631dcca526108a1278889c"}, + {file = "zlib_state-0.1.9-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f76bfc69636b24c26521bbb2bacd26a3826ff57112d0b7801662a122ea98bec1"}, + {file = "zlib_state-0.1.9-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b697d58ef519dbbe76361c0d37a605f599660119c2ddac70811995ebdcc8cc20"}, + {file = "zlib_state-0.1.9-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8773ff5130999017178bdc52bea27d48eb9c78d2f113547a317474a5d24665e6"}, + {file = "zlib_state-0.1.9-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c27962a41070c863a9464a4e663df7716feb42cf28bec3dbafa35c68f7bd1b0"}, + {file = "zlib_state-0.1.9-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3cdbffc1808590b03eb656f5eda2fd83f069e345ed0fdab90681252ab64b7ad"}, + {file = "zlib_state-0.1.9.tar.gz", hash = "sha256:8baef0cd0ab9f9d556a35df3f57b8d0f8b4a49c3f028189ab401672939cf435d"}, +] + [extras] bedrock = ["boto3"] cohere = ["cohere"] mistralai = ["mistralai"] +nltk = ["nltk"] openai = ["openai"] +ranx = ["ranx"] sentence-transformers = ["scipy", "scipy", "sentence-transformers"] vertexai = ["google-cloud-aiplatform", "protobuf"] voyageai = ["voyageai"] @@ -6257,4 +7719,4 @@ voyageai = ["voyageai"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<3.14" -content-hash = "bca983a493824ac52308e35cd29108d855d868c953cf1375aa01a1a90938c826" +content-hash = "52db593aa7e849c77d41201044adb215e57151dfe8c2da7be4ddfe381b27e230" diff --git a/pyproject.toml b/pyproject.toml index 64b4cda9..4e3934c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "redisvl" -version = "0.4.1" +version = "0.5.0" description = "Python client library and CLI for using Redis as a vector database" authors = ["Redis Inc. "] license = "MIT" @@ -33,6 +33,8 @@ tenacity = ">=8.2.2" tabulate = "^0.9.0" ml-dtypes = "^0.4.0" python-ulid = "^3.0.0" +nltk = { version = "^3.8.1", optional = true } +jsonpath-ng = "^1.5.0" openai = { version = "^1.13.0", optional = true } sentence-transformers = { version = "^3.4.0", optional = true } scipy = [ @@ -44,7 +46,8 @@ protobuf = { version = "^5.29.1", optional = true } cohere = { version = ">=4.44", optional = true } mistralai = { version = ">=1.0.0", optional = true } voyageai = { version = ">=0.2.2", optional = true } -boto3 = { version = "^1.36.0", optional = true, extras = ["bedrock"] } +ranx = { version = "^0.3.0", python=">=3.10", optional = true } +boto3 = {version = "1.36.0", optional = true, extras = ["bedrock"]} [tool.poetry.extras] openai = ["openai"] @@ -53,7 +56,9 @@ vertexai = ["google_cloud_aiplatform", "protobuf"] cohere = ["cohere"] mistralai = ["mistralai"] voyageai = ["voyageai"] +ranx = ["ranx"] bedrock = ["boto3"] +nltk = ["nltk"] [tool.poetry.group.dev.dependencies] black = "^25.1.0" diff --git a/redisvl/exceptions.py b/redisvl/exceptions.py index e645e3e2..f8917c3d 100644 --- a/redisvl/exceptions.py +++ b/redisvl/exceptions.py @@ -1,10 +1,32 @@ -class RedisVLException(Exception): - """Base RedisVL exception""" +""" +RedisVL Exception Classes +This module defines all custom exceptions used throughout the RedisVL library. +""" -class RedisModuleVersionError(RedisVLException): - """Invalid module versions installed""" +class RedisVLError(Exception): + """Base exception for all RedisVL errors.""" -class RedisSearchError(RedisVLException): - """Error while performing a search or aggregate request""" + pass + + +class RedisModuleVersionError(RedisVLError): + """Error raised when required Redis modules are missing or have incompatible versions.""" + + pass + + +class RedisSearchError(RedisVLError): + """Error raised for Redis Search specific operations.""" + + pass + + +class SchemaValidationError(RedisVLError): + """Error when validating data against a schema.""" + + def __init__(self, message, index=None): + if index is not None: + message = f"Validation failed for object at index {index}: {message}" + super().__init__(message) diff --git a/redisvl/extensions/llmcache/semantic.py b/redisvl/extensions/llmcache/semantic.py index c741ca45..13bab707 100644 --- a/redisvl/extensions/llmcache/semantic.py +++ b/redisvl/extensions/llmcache/semantic.py @@ -2,6 +2,7 @@ import weakref from typing import Any, Dict, List, Optional +import numpy as np from redis import Redis from redisvl.extensions.constants import ( @@ -21,8 +22,9 @@ SemanticCacheIndexSchema, ) from redisvl.index import AsyncSearchIndex, SearchIndex -from redisvl.query import RangeQuery +from redisvl.query import VectorRangeQuery from redisvl.query.filter import FilterExpression +from redisvl.query.query import BaseQuery from redisvl.redis.connection import RedisConnectionFactory from redisvl.utils.log import get_logger from redisvl.utils.utils import ( @@ -93,12 +95,8 @@ def __init__( } # Use the index name as the key prefix by default - if "prefix" in kwargs: - prefix = kwargs["prefix"] - else: - prefix = name - - dtype = kwargs.get("dtype") + prefix = kwargs.pop("prefix", name) + dtype = kwargs.pop("dtype", None) # Validate a provided vectorizer or set the default if vectorizer: @@ -109,7 +107,10 @@ def __init__( f"Provided dtype {dtype} does not match vectorizer dtype {vectorizer.dtype}" ) else: - vectorizer_kwargs = {"dtype": dtype} if dtype else {} + vectorizer_kwargs = kwargs + + if dtype: + vectorizer_kwargs.update(**{"dtype": dtype}) vectorizer = HFTextVectorizer( model="sentence-transformers/all-mpnet-base-v2", @@ -236,9 +237,9 @@ def set_threshold(self, distance_threshold: float) -> None: Raises: ValueError: If the threshold is not between 0 and 1. """ - if not 0 <= float(distance_threshold) <= 1: + if not 0 <= float(distance_threshold) <= 2: raise ValueError( - f"Distance must be between 0 and 1, got {distance_threshold}" + f"Distance must be between 0 and 2, got {distance_threshold}" ) self._distance_threshold = float(distance_threshold) @@ -388,7 +389,7 @@ def check( vector = vector or self._vectorize_prompt(prompt) self._check_vector_dims(vector) - query = RangeQuery( + query = VectorRangeQuery( vector=vector, vector_field_name=CACHE_VECTOR_FIELD_NAME, return_fields=self.return_fields, @@ -471,7 +472,7 @@ async def acheck( vector = vector or await self._avectorize_prompt(prompt) self._check_vector_dims(vector) - query = RangeQuery( + query = VectorRangeQuery( vector=vector, vector_field_name=CACHE_VECTOR_FIELD_NAME, return_fields=self.return_fields, @@ -479,6 +480,7 @@ async def acheck( num_results=num_results, return_score=True, filter_expression=filter_expression, + normalize_vector_distance=True, ) # Search the cache! diff --git a/redisvl/extensions/router/schema.py b/redisvl/extensions/router/schema.py index 1b1d6dc8..d9b38677 100644 --- a/redisvl/extensions/router/schema.py +++ b/redisvl/extensions/router/schema.py @@ -18,7 +18,7 @@ class Route(BaseModel): """List of reference phrases for the route.""" metadata: Dict[str, Any] = Field(default={}) """Metadata associated with the route.""" - distance_threshold: Annotated[float, Field(strict=True, gt=0, le=1)] = 0.5 + distance_threshold: Annotated[float, Field(strict=True, gt=0, le=2)] = 0.5 """Distance threshold for matching the route.""" @field_validator("name") diff --git a/redisvl/extensions/router/semantic.py b/redisvl/extensions/router/semantic.py index 8aff7524..9ca886ab 100644 --- a/redisvl/extensions/router/semantic.py +++ b/redisvl/extensions/router/semantic.py @@ -17,7 +17,7 @@ SemanticRouterIndexSchema, ) from redisvl.index import SearchIndex -from redisvl.query import RangeQuery +from redisvl.query import VectorRangeQuery from redisvl.redis.utils import convert_bytes, hashify, make_dict from redisvl.utils.log import get_logger from redisvl.utils.utils import deprecated_argument, model_to_dict @@ -72,7 +72,7 @@ def __init__( connection_kwargs (Dict[str, Any]): The connection arguments for the redis client. Defaults to empty {}. """ - dtype = kwargs.get("dtype") + dtype = kwargs.pop("dtype", None) # Validate a provided vectorizer or set the default if vectorizer: @@ -83,8 +83,15 @@ def __init__( f"Provided dtype {dtype} does not match vectorizer dtype {vectorizer.dtype}" ) else: - vectorizer_kwargs = {"dtype": dtype} if dtype else {} - vectorizer = HFTextVectorizer(**vectorizer_kwargs) + vectorizer_kwargs = kwargs + + if dtype: + vectorizer_kwargs.update(**{"dtype": dtype}) + + vectorizer = HFTextVectorizer( + model="sentence-transformers/all-mpnet-base-v2", + **vectorizer_kwargs, + ) if routing_config is None: routing_config = RoutingConfig() @@ -160,6 +167,16 @@ def update_routing_config(self, routing_config: RoutingConfig): """ self.routing_config = routing_config + def update_route_thresholds(self, route_thresholds: Dict[str, Optional[float]]): + """Update the distance thresholds for each route. + + Args: + route_thresholds (Dict[str, float]): Dictionary of route names and their distance thresholds. + """ + for route in self.routes: + if route.name in route_thresholds: + route.distance_threshold = route_thresholds[route.name] # type: ignore + def _route_ref_key(self, route_name: str, reference: str) -> str: """Generate the route reference key.""" reference_hash = hashify(reference) @@ -227,7 +244,7 @@ def _distance_threshold_filter(self) -> str: def _build_aggregate_request( self, - vector_range_query: RangeQuery, + vector_range_query: VectorRangeQuery, aggregation_method: DistanceAggregationMethod, max_k: int, ) -> AggregateRequest: @@ -263,18 +280,16 @@ def _get_route_matches( aggregation_method: DistanceAggregationMethod, max_k: int = 1, ) -> List[RouteMatch]: - """Get the route matches for a given vector and aggregation method.""" + """Get route response from vector db""" - thresholds = [route.distance_threshold for route in self.routes] - if thresholds: - distance_threshold = max(thresholds) - else: - raise ValueError("No distance thresholds provided for the semantic router") + # what's interesting about this is that we only provide one distance_threshold for a range query not multiple + # therefore you might take the max_threshold and further refine from there. + distance_threshold = max(route.distance_threshold for route in self.routes) - vector_range_query = RangeQuery( + vector_range_query = VectorRangeQuery( vector=vector, vector_field_name=ROUTE_VECTOR_FIELD_NAME, - distance_threshold=distance_threshold, + distance_threshold=float(distance_threshold), return_fields=["route_name"], ) @@ -329,7 +344,7 @@ def _classify_multi_route( ) -> List[RouteMatch]: """Classify to multiple routes, up to max_k (int), using a vector.""" - route_matches = self._get_route_matches(vector, aggregation_method, max_k) + route_matches = self._get_route_matches(vector, aggregation_method, max_k=max_k) # process route matches top_route_matches: List[RouteMatch] = [] diff --git a/redisvl/extensions/session_manager/semantic_session.py b/redisvl/extensions/session_manager/semantic_session.py index 1aa15315..9497d06c 100644 --- a/redisvl/extensions/session_manager/semantic_session.py +++ b/redisvl/extensions/session_manager/semantic_session.py @@ -71,7 +71,7 @@ def __init__( super().__init__(name, session_tag) prefix = prefix or name - dtype = kwargs.get("dtype") + dtype = kwargs.pop("dtype", None) # Validate a provided vectorizer or set the default if vectorizer: @@ -82,10 +82,13 @@ def __init__( f"Provided dtype {dtype} does not match vectorizer dtype {vectorizer.dtype}" ) else: - vectorizer_kwargs = {"dtype": dtype} if dtype else {} + vectorizer_kwargs = kwargs + + if dtype: + vectorizer_kwargs.update(**{"dtype": dtype}) vectorizer = HFTextVectorizer( - model="sentence-transformers/msmarco-distilbert-cos-v5", + model="sentence-transformers/all-mpnet-base-v2", **vectorizer_kwargs, ) diff --git a/redisvl/index/index.py b/redisvl/index/index.py index c4e5de62..34bdaf77 100644 --- a/redisvl/index/index.py +++ b/redisvl/index/index.py @@ -18,6 +18,7 @@ Union, ) +from redisvl.redis.utils import convert_bytes, make_dict from redisvl.utils.utils import deprecated_argument, deprecated_function, sync_wrapper if TYPE_CHECKING: @@ -32,9 +33,21 @@ from redis.commands.helpers import get_protocol_version # type: ignore from redis.commands.search.indexDefinition import IndexDefinition -from redisvl.exceptions import RedisModuleVersionError, RedisSearchError +from redisvl.exceptions import ( + RedisModuleVersionError, + RedisSearchError, + RedisVLError, + SchemaValidationError, +) from redisvl.index.storage import BaseStorage, HashStorage, JsonStorage -from redisvl.query import BaseQuery, CountQuery, FilterQuery +from redisvl.query import ( + AggregationQuery, + BaseQuery, + BaseVectorQuery, + CountQuery, + FilterQuery, + HybridQuery, +) from redisvl.query.filter import FilterExpression from redisvl.redis.connection import ( RedisConnectionFactory, @@ -42,6 +55,7 @@ ) from redisvl.redis.utils import convert_bytes from redisvl.schema import IndexSchema, StorageType +from redisvl.schema.fields import VECTOR_NORM_MAP, VectorDistanceMetric from redisvl.utils.log import get_logger logger = get_logger(__name__) @@ -62,7 +76,7 @@ def process_results( - results: "Result", query: BaseQuery, storage_type: StorageType + results: "Result", query: BaseQuery, schema: IndexSchema ) -> List[Dict[str, Any]]: """Convert a list of search Result objects into a list of document dictionaries. @@ -87,11 +101,24 @@ def process_results( # Determine if unpacking JSON is needed unpack_json = ( - (storage_type == StorageType.JSON) + (schema.index.storage_type == StorageType.JSON) and isinstance(query, FilterQuery) and not query._return_fields # type: ignore ) + if (isinstance(query, BaseVectorQuery)) and query._normalize_vector_distance: + dist_metric = VectorDistanceMetric( + schema.fields[query._vector_field_name].attrs.distance_metric.upper() # type: ignore + ) + if dist_metric == VectorDistanceMetric.IP: + warnings.warn( + "Attempting to normalize inner product distance metric. Use cosine distance instead which is normalized inner product by definition." + ) + + norm_fn = VECTOR_NORM_MAP[dist_metric.value] + else: + norm_fn = None + # Process records def _process(doc: "Document") -> Dict[str, Any]: doc_dict = doc.__dict__ @@ -105,6 +132,12 @@ def _process(doc: "Document") -> Dict[str, Any]: return {"id": doc_dict.get("id"), **json_data} raise ValueError(f"Unable to parse json data from Redis {json_data}") + if norm_fn: + # convert float back to string to be consistent + doc_dict[query.DISTANCE_ID] = str( # type: ignore + norm_fn(float(doc_dict[query.DISTANCE_ID])) # type: ignore + ) + # Remove 'payload' if present doc_dict.pop("payload", None) @@ -113,6 +146,34 @@ def _process(doc: "Document") -> Dict[str, Any]: return [_process(doc) for doc in results.docs] +def process_aggregate_results( + results: "AggregateResult", query: AggregationQuery, storage_type: StorageType +) -> List[Dict[str, Any]]: + """Convert an aggregate reslt object into a list of document dictionaries. + + This function processes results from Redis, handling different storage + types and query types. For JSON storage with empty return fields, it + unpacks the JSON object while retaining the document ID. The 'payload' + field is also removed from all resulting documents for consistency. + + Args: + results (AggregateResult): The aggregate results from Redis. + query (AggregationQuery): The aggregation query object used for the aggregation. + storage_type (StorageType): The storage type of the search + index (json or hash). + + Returns: + List[Dict[str, Any]]: A list of processed document dictionaries. + """ + + def _process(row): + result = make_dict(convert_bytes(row)) + result.pop("__score", None) + return result + + return [_process(r) for r in results.rows] + + class BaseSearchIndex: """Base search engine class""" @@ -130,8 +191,7 @@ def __init__(*args, **kwargs): def _storage(self) -> BaseStorage: """The storage type for the index schema.""" return self._STORAGE_MAP[self.schema.index.storage_type]( - prefix=self.schema.index.prefix, - key_separator=self.schema.index.key_separator, + index_schema=self.schema ) @property @@ -171,7 +231,7 @@ def from_yaml(cls, schema_path: str, **kwargs): from redisvl.index import SearchIndex - index = SearchIndex.from_yaml("schemas/schema.yaml") + index = SearchIndex.from_yaml("schemas/schema.yaml", redis_url="redis://localhost:6379") """ schema = IndexSchema.from_yaml(schema_path) return cls(schema=schema, **kwargs) @@ -199,7 +259,7 @@ def from_dict(cls, schema_dict: Dict[str, Any], **kwargs): "fields": [ {"name": "doc-id", "type": "tag"} ] - }) + }, redis_url="redis://localhost:6379") """ schema = IndexSchema.from_dict(schema_dict) @@ -243,10 +303,14 @@ class SearchIndex(BaseSearchIndex): from redisvl.index import SearchIndex # initialize the index object with schema from file - index = SearchIndex.from_yaml("schemas/schema.yaml", redis_url="redis://localhost:6379") + index = SearchIndex.from_yaml( + "schemas/schema.yaml", + redis_url="redis://localhost:6379", + validate_on_load=True + ) # create the index - index.create(overwrite=True) + index.create(overwrite=True, drop=False) # data is an iterable of dictionaries index.load(data) @@ -263,6 +327,7 @@ def __init__( redis_client: Optional[redis.Redis] = None, redis_url: Optional[str] = None, connection_kwargs: Optional[Dict[str, Any]] = None, + validate_on_load: bool = False, **kwargs, ): """Initialize the RedisVL search index with a schema, Redis client @@ -277,6 +342,8 @@ def __init__( connect to. connection_kwargs (Dict[str, Any], optional): Redis client connection args. + validate_on_load (bool, optional): Whether to validate data against schema + when loading. Defaults to False. """ if "connection_args" in kwargs: connection_kwargs = kwargs.pop("connection_args") @@ -285,7 +352,7 @@ def __init__( raise ValueError("Must provide a valid IndexSchema object") self.schema = schema - + self._validate_on_load = validate_on_load self._lib_name: Optional[str] = kwargs.pop("lib_name", None) # Store connection parameters @@ -400,11 +467,6 @@ def connect(self, redis_url: Optional[str] = None, **kwargs): ValueError: If the Redis URL is not provided nor accessible through the `REDIS_URL` environment variable. ModuleNotFoundError: If required Redis modules are not installed. - - .. code-block:: python - - index.connect(redis_url="redis://localhost:6379") - """ self.__redis_client = RedisConnectionFactory.get_redis_connection( redis_url=redis_url, **kwargs @@ -424,16 +486,6 @@ def set_client(self, redis_client: redis.Redis, **kwargs): Raises: TypeError: If the provided client is not valid. - - .. code-block:: python - - import redis - from redisvl.index import SearchIndex - - client = redis.Redis.from_url("redis://localhost:6379") - index = SearchIndex.from_yaml("schemas/schema.yaml") - index.set_client(client) - """ RedisConnectionFactory.validate_sync_redis(redis_client) self.__redis_client = redis_client @@ -592,27 +644,8 @@ def load( List[str]: List of keys loaded to Redis. Raises: - ValueError: If the length of provided keys does not match the length - of objects. - - .. code-block:: python - - data = [{"test": "foo"}, {"test": "bar"}] - - # simple case - keys = index.load(data) - - # set 360 second ttl policy on data - keys = index.load(data, ttl=360) - - # load data with predefined keys - keys = index.load(data, keys=["rvl:foo", "rvl:bar"]) - - # load data with preprocessing step - def add_field(d): - d["new_field"] = 123 - return d - keys = index.load(data, preprocess=add_field) + SchemaValidationError: If validation fails when validate_on_load is enabled. + RedisVLError: If there's an error loading data to Redis. """ try: return self._storage.write( @@ -623,10 +656,16 @@ def add_field(d): ttl=ttl, preprocess=preprocess, batch_size=batch_size, + validate=self._validate_on_load, ) - except: - logger.exception("Error while loading data to Redis") + except SchemaValidationError: + # Pass through validation errors directly + logger.exception("Schema validation error while loading data") raise + except Exception as e: + # Wrap other errors as general RedisVL errors + logger.exception("Error while loading data to Redis") + raise RedisVLError(f"Failed to load data: {str(e)}") from e def fetch(self, id: str) -> Optional[Dict[str, Any]]: """Fetch an object from Redis by id. @@ -647,6 +686,17 @@ def fetch(self, id: str) -> Optional[Dict[str, Any]]: return convert_bytes(obj[0]) return None + def _aggregate(self, aggregation_query: AggregationQuery) -> List[Dict[str, Any]]: + """Execute an aggretation query and processes the results.""" + results = self.aggregate( + aggregation_query, query_params=aggregation_query.params # type: ignore[attr-defined] + ) + return process_aggregate_results( + results, + query=aggregation_query, + storage_type=self.schema.index.storage_type, + ) + def aggregate(self, *args, **kwargs) -> "AggregateResult": """Perform an aggregation operation against the index. @@ -757,11 +807,7 @@ def batch_query( ) all_parsed = [] for query, batch_results in zip(queries, results): - parsed = process_results( - batch_results, - query=query, - storage_type=self.schema.index.storage_type, - ) + parsed = process_results(batch_results, query=query, schema=self.schema) # Create separate lists of parsed results for each query # passed in to the batch_search method, so that callers can # access the results for each query individually @@ -771,18 +817,16 @@ def batch_query( def _query(self, query: BaseQuery) -> List[Dict[str, Any]]: """Execute a query and process results.""" results = self.search(query.query, query_params=query.params) - return process_results( - results, query=query, storage_type=self.schema.index.storage_type - ) + return process_results(results, query=query, schema=self.schema) - def query(self, query: BaseQuery) -> List[Dict[str, Any]]: + def query(self, query: Union[BaseQuery, AggregationQuery]) -> List[Dict[str, Any]]: """Execute a query on the index. - This method takes a BaseQuery object directly, runs the search, and + This method takes a BaseQuery or AggregationQuery object directly, and handles post-processing of the search. Args: - query (BaseQuery): The query to run. + query (Union[BaseQuery, AggregateQuery]): The query to run. Returns: List[Result]: A list of search results. @@ -800,7 +844,10 @@ def query(self, query: BaseQuery) -> List[Dict[str, Any]]: results = index.query(query) """ - return self._query(query) + if isinstance(query, AggregationQuery): + return self._aggregate(query) + else: + return self._query(query) def paginate(self, query: BaseQuery, page_size: int = 30) -> Generator: """Execute a given query against the index and return results in @@ -912,11 +959,12 @@ class AsyncSearchIndex(BaseSearchIndex): # initialize the index object with schema from file index = AsyncSearchIndex.from_yaml( "schemas/schema.yaml", - redis_url="redis://localhost:6379" + redis_url="redis://localhost:6379", + validate_on_load=True ) # create the index - await index.create(overwrite=True) + await index.create(overwrite=True, drop=False) # data is an iterable of dictionaries await index.load(data) @@ -934,6 +982,7 @@ def __init__( redis_url: Optional[str] = None, redis_client: Optional[aredis.Redis] = None, connection_kwargs: Optional[Dict[str, Any]] = None, + validate_on_load: bool = False, **kwargs, ): """Initialize the RedisVL async search index with a schema. @@ -946,6 +995,8 @@ def __init__( instantiated redis client. connection_kwargs (Optional[Dict[str, Any]]): Redis client connection args. + validate_on_load (bool, optional): Whether to validate data against schema + when loading. Defaults to False. """ if "redis_kwargs" in kwargs: connection_kwargs = kwargs.pop("redis_kwargs") @@ -955,7 +1006,7 @@ def __init__( raise ValueError("Must provide a valid IndexSchema object") self.schema = schema - + self._validate_on_load = validate_on_load self._lib_name: Optional[str] = kwargs.pop("lib_name", None) # Store connection parameters @@ -1203,6 +1254,7 @@ async def expire_keys( else: return await client.expire(keys, ttl) + @deprecated_argument("concurrency", "Use batch_size instead.") async def load( self, data: Iterable[Any], @@ -1211,9 +1263,10 @@ async def load( ttl: Optional[int] = None, preprocess: Optional[Callable] = None, concurrency: Optional[int] = None, + batch_size: Optional[int] = None, ) -> List[str]: - """Asynchronously load objects to Redis with concurrency control. - Returns the list of keys loaded to Redis. + """Asynchronously load objects to Redis. Returns the list of keys loaded + to Redis. RedisVL automatically handles constructing the object keys, batching, optional preprocessing steps, and setting optional expiration @@ -1228,18 +1281,18 @@ async def load( Must match the length of objects if provided. Defaults to None. ttl (Optional[int], optional): Time-to-live in seconds for each key. Defaults to None. - preprocess (Optional[Callable], optional): An async function to + preprocess (Optional[Callable], optional): A function to preprocess objects before storage. Defaults to None. - concurrency (Optional[int], optional): The maximum number of - concurrent write operations. Defaults to class's default - concurrency level. + batch_size (Optional[int], optional): Number of objects to write in + a single Redis pipeline execution. Defaults to class's + default batch size. Returns: List[str]: List of keys loaded to Redis. Raises: - ValueError: If the length of provided keys does not match the - length of objects. + SchemaValidationError: If validation fails when validate_on_load is enabled. + RedisVLError: If there's an error loading data to Redis. .. code-block:: python @@ -1255,7 +1308,7 @@ async def load( keys = await index.load(data, keys=["rvl:foo", "rvl:bar"]) # load data with preprocessing step - async def add_field(d): + def add_field(d): d["new_field"] = 123 return d keys = await index.load(data, preprocess=add_field) @@ -1270,11 +1323,17 @@ async def add_field(d): keys=keys, ttl=ttl, preprocess=preprocess, - concurrency=concurrency, + batch_size=batch_size, + validate=self._validate_on_load, ) - except: - logger.exception("Error while loading data to Redis") + except SchemaValidationError: + # Pass through validation errors directly + logger.exception("Schema validation error while loading data") raise + except Exception as e: + # Wrap other errors as general RedisVL errors + logger.exception("Error while loading data to Redis") + raise RedisVLError(f"Failed to load data: {str(e)}") from e async def fetch(self, id: str) -> Optional[Dict[str, Any]]: """Asynchronously etch an object from Redis by id. The id is typically @@ -1294,6 +1353,19 @@ async def fetch(self, id: str) -> Optional[Dict[str, Any]]: return convert_bytes(obj[0]) return None + async def _aggregate( + self, aggregation_query: AggregationQuery + ) -> List[Dict[str, Any]]: + """Execute an aggretation query and processes the results.""" + results = await self.aggregate( + aggregation_query, query_params=aggregation_query.params # type: ignore[attr-defined] + ) + return process_aggregate_results( + results, + query=aggregation_query, + storage_type=self.schema.index.storage_type, + ) + async def aggregate(self, *args, **kwargs) -> "AggregateResult": """Perform an aggregation operation against the index. @@ -1403,7 +1475,7 @@ async def batch_query( parsed = process_results( batch_results, query=query, - storage_type=self.schema.index.storage_type, + schema=self.schema, ) # Create separate lists of parsed results for each query # passed in to the batch_search method, so that callers can @@ -1415,18 +1487,18 @@ async def batch_query( async def _query(self, query: BaseQuery) -> List[Dict[str, Any]]: """Asynchronously execute a query and process results.""" results = await self.search(query.query, query_params=query.params) - return process_results( - results, query=query, storage_type=self.schema.index.storage_type - ) + return process_results(results, query=query, schema=self.schema) - async def query(self, query: BaseQuery) -> List[Dict[str, Any]]: + async def query( + self, query: Union[BaseQuery, AggregationQuery] + ) -> List[Dict[str, Any]]: """Asynchronously execute a query on the index. - This method takes a BaseQuery object directly, runs the search, and - handles post-processing of the search. + This method takes a BaseQuery or AggregationQuery object directly, runs + the search, and handles post-processing of the search. Args: - query (BaseQuery): The query to run. + query (Union[BaseQuery, AggregateQuery]): The query to run. Returns: List[Result]: A list of search results. @@ -1443,7 +1515,10 @@ async def query(self, query: BaseQuery) -> List[Dict[str, Any]]: results = await index.query(query) """ - return await self._query(query) + if isinstance(query, AggregationQuery): + return await self._aggregate(query) + else: + return await self._query(query) async def paginate(self, query: BaseQuery, page_size: int = 30) -> AsyncGenerator: """Execute a given query against the index and return results in diff --git a/redisvl/index/storage.py b/redisvl/index/storage.py index 2be386c0..792b6bc4 100644 --- a/redisvl/index/storage.py +++ b/redisvl/index/storage.py @@ -1,14 +1,19 @@ -import asyncio -from typing import Any, Callable, Dict, Iterable, List, Optional +from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple -from pydantic import BaseModel +from pydantic import BaseModel, ValidationError from redis import Redis from redis.asyncio import Redis as AsyncRedis from redis.commands.search.indexDefinition import IndexType +from redisvl.exceptions import SchemaValidationError from redisvl.redis.utils import convert_bytes +from redisvl.schema import IndexSchema +from redisvl.schema.validation import validate_object +from redisvl.utils.log import get_logger from redisvl.utils.utils import create_ulid +logger = get_logger(__name__) + class BaseStorage(BaseModel): """ @@ -20,14 +25,10 @@ class BaseStorage(BaseModel): type: IndexType """Type of index used in storage""" - prefix: str - """Prefix for Redis keys""" - key_separator: str - """Separator between prefix and key value""" + index_schema: IndexSchema + """Index schema definition""" default_batch_size: int = 200 """Default size for batch operations""" - default_write_concurrency: int = 20 - """Default concurrency for async ops""" @staticmethod def _key(id: str, prefix: str, key_separator: str) -> str: @@ -72,7 +73,9 @@ def _create_key(self, obj: Dict[str, Any], id_field: Optional[str] = None) -> st raise ValueError(f"Key field {id_field} not found in record {obj}") return self._key( - key_value, prefix=self.prefix, key_separator=self.key_separator + key_value, + prefix=self.index_schema.index.prefix, + key_separator=self.index_schema.index.key_separator, ) @staticmethod @@ -92,35 +95,6 @@ def _preprocess(obj: Any, preprocess: Optional[Callable] = None) -> Dict[str, An obj = preprocess(obj) return obj - @staticmethod - async def _apreprocess( - obj: Any, preprocess: Optional[Callable] = None - ) -> Dict[str, Any]: - """Asynchronously apply a preprocessing function to the object if - provided. - - Args: - preprocess (Optional[Callable], optional): Async function to - process the object. - obj (Any): Object to preprocess. - - Returns: - Dict[str, Any]: Processed object as a dictionary. - """ - # optionally async preprocess object - if preprocess: - obj = await preprocess(obj) - return obj - - def _validate(self, obj: Dict[str, Any]): - """Validate the object before writing to Redis. This method should be - implemented by subclasses. - - Args: - obj (Dict[str, Any]): The object to validate. - """ - raise NotImplementedError - @staticmethod def _set(client: Redis, key: str, obj: Dict[str, Any]): """Synchronously set the value in Redis for the given key. @@ -169,6 +143,81 @@ async def _aget(client: AsyncRedis, key: str) -> Dict[str, Any]: """ raise NotImplementedError + def _validate(self, obj: Dict[str, Any]) -> Dict[str, Any]: + """ + Validate an object against the schema using Pydantic-based validation. + + Args: + obj: The object to validate + + Returns: + Validated object with any type coercions applied + + Raises: + ValueError: If validation fails + """ + # Pass directly to validation function and let any errors propagate + return validate_object(self.index_schema, obj) + + def _preprocess_and_validate_objects( + self, + objects: Iterable[Any], + id_field: Optional[str] = None, + keys: Optional[Iterable[str]] = None, + preprocess: Optional[Callable] = None, + validate: bool = False, + ) -> List[Tuple[str, Dict[str, Any]]]: + """ + Preprocess and validate a list of objects with fail-fast approach. + + Args: + objects: List of objects to preprocess and validate + id_field: Field to use as the key + keys: Optional iterable of keys + preprocess: Optional preprocessing function + validate: Whether to validate against schema + + Returns: + List of tuples (key, processed_obj) for valid objects + + Raises: + SchemaValidationError: If validation fails, with context about which object failed + ValueError: If any other processing errors occur + """ + prepared_objects = [] + keys_iterator = iter(keys) if keys else None + + for i, obj in enumerate(objects): + try: + # Generate key + key = ( + next(keys_iterator) + if keys_iterator + else self._create_key(obj, id_field) + ) + + # Preprocess + processed_obj = self._preprocess(obj, preprocess) + + # Schema validation if enabled + if validate: + processed_obj = self._validate(processed_obj) + + # Store valid object with its key for writing + prepared_objects.append((key, processed_obj)) + + except ValidationError as e: + # Convert Pydantic ValidationError to SchemaValidationError with index context + raise SchemaValidationError(str(e), index=i) from e + except Exception as e: + # Capture other exceptions with context + object_id = f"at index {i}" + raise ValueError( + f"Error processing object {object_id}: {str(e)}" + ) from e + + return prepared_objects + def write( self, redis_client: Redis, @@ -178,6 +227,7 @@ def write( ttl: Optional[int] = None, preprocess: Optional[Callable] = None, batch_size: Optional[int] = None, + validate: bool = False, ) -> List[str]: """Write a batch of objects to Redis as hash entries. This method returns a list of Redis keys written to the database. @@ -195,44 +245,52 @@ def write( objects before storage. Defaults to None. batch_size (Optional[int], optional): Number of objects to write in a single Redis pipeline execution. + validate (bool, optional): Whether to validate objects against schema. + Defaults to False. Raises: ValueError: If the length of provided keys does not match the - length of objects. + length of objects, or if validation fails. """ if keys and len(keys) != len(objects): # type: ignore raise ValueError("Length of keys does not match the length of objects") if batch_size is None: - # Use default or calculate based on the input data batch_size = self.default_batch_size - keys_iterator = iter(keys) if keys else None - added_keys: List[str] = [] - - if objects: - with redis_client.pipeline(transaction=False) as pipe: - for i, obj in enumerate(objects, start=1): - # Construct key, validate, and write - key = ( - next(keys_iterator) - if keys_iterator - else self._create_key(obj, id_field) - ) - obj = self._preprocess(obj, preprocess) - self._validate(obj) - self._set(pipe, key, obj) - # Set TTL if provided - if ttl: - pipe.expire(key, ttl) - # Execute mini batch - if i % batch_size == 0: - pipe.execute() - added_keys.append(key) - # Clean up batches if needed - if i % batch_size != 0: + if not objects: + return [] + + # Pass 1: Preprocess and validate all objects + prepared_objects = self._preprocess_and_validate_objects( + list(objects), # Convert Iterable to List + id_field=id_field, + keys=keys, + preprocess=preprocess, + validate=validate, + ) + + # Pass 2: Write all valid objects in batches + added_keys = [] + + with redis_client.pipeline(transaction=False) as pipe: + for i, (key, obj) in enumerate(prepared_objects, start=1): + self._set(pipe, key, obj) + + # Set TTL if provided + if ttl: + pipe.expire(key, ttl) + + added_keys.append(key) + + # Execute in batches + if i % batch_size == 0: pipe.execute() + # Execute any remaining commands + if len(prepared_objects) % batch_size != 0: + pipe.execute() + return added_keys async def awrite( @@ -242,12 +300,12 @@ async def awrite( id_field: Optional[str] = None, keys: Optional[Iterable[str]] = None, ttl: Optional[int] = None, + batch_size: Optional[int] = None, preprocess: Optional[Callable] = None, - concurrency: Optional[int] = None, + validate: bool = False, ) -> List[str]: - """Asynchronously write objects to Redis as hash entries with - concurrency control. The method returns a list of keys written to the - database. + """Asynchronously write objects to Redis as hash entries using pipeline batching. + The method returns a list of keys written to the database. Args: redis_client (AsyncRedis): An asynchronous Redis client used @@ -259,47 +317,60 @@ async def awrite( Must match the length of objects if provided. ttl (Optional[int], optional): Time-to-live in seconds for each key. Defaults to None. + batch_size (Optional[int], optional): Number of objects to write + in a single Redis pipeline execution. preprocess (Optional[Callable], optional): An async function to preprocess objects before storage. Defaults to None. - concurrency (Optional[int], optional): The maximum number of - concurrent write operations. Defaults to class's default - concurrency level. + validate (bool, optional): Whether to validate objects against schema. + Defaults to False. Returns: List[str]: List of Redis keys loaded to the databases. Raises: ValueError: If the length of provided keys does not match the - length of objects. + length of objects, or if validation fails. """ if keys and len(keys) != len(objects): # type: ignore raise ValueError("Length of keys does not match the length of objects") - if not concurrency: - concurrency = self.default_write_concurrency + if batch_size is None: + batch_size = self.default_batch_size - semaphore = asyncio.Semaphore(concurrency) - keys_iterator = iter(keys) if keys else None + if not objects: + return [] + + # Pass 1: Preprocess and validate all objects + prepared_objects = self._preprocess_and_validate_objects( + list(objects), # Convert Iterable to List + id_field=id_field, + keys=keys, + preprocess=preprocess, + validate=validate, + ) - async def _load(obj: Dict[str, Any], key: Optional[str] = None) -> str: - async with semaphore: - if key is None: - key = self._create_key(obj, id_field) - obj = await self._apreprocess(obj, preprocess) - self._validate(obj) - await self._aset(redis_client, key, obj) + # Pass 2: Write all valid objects in batches using pipeline + added_keys = [] + + async with redis_client.pipeline(transaction=False) as pipe: + for i, (key, obj) in enumerate(prepared_objects, start=1): + await self._aset(pipe, key, obj) + + # Set TTL if provided if ttl: - await redis_client.expire(key, ttl) - return key + await pipe.expire(key, ttl) - if keys_iterator: - tasks = [ - asyncio.create_task(_load(obj, next(keys_iterator))) for obj in objects - ] - else: - tasks = [asyncio.create_task(_load(obj)) for obj in objects] + added_keys.append(key) - return await asyncio.gather(*tasks) + # Execute in batches + if i % batch_size == 0: + await pipe.execute() + + # Execute any remaining commands + if len(prepared_objects) % batch_size != 0: + await pipe.execute() + + return added_keys def get( self, redis_client: Redis, keys: Iterable[str], batch_size: Optional[int] = None @@ -325,9 +396,7 @@ def get( return [] if batch_size is None: - batch_size = ( - self.default_batch_size - ) # Use default or calculate based on the input data + batch_size = self.default_batch_size # Use a pipeline to batch the retrieval with redis_client.pipeline(transaction=False) as pipe: @@ -345,39 +414,42 @@ async def aget( self, redis_client: AsyncRedis, keys: Iterable[str], - concurrency: Optional[int] = None, + batch_size: Optional[int] = None, ) -> List[Dict[str, Any]]: - """Asynchronously retrieve objects from Redis by keys, with concurrency - control. + """Asynchronously retrieve objects from Redis by keys. Args: redis_client (AsyncRedis): Asynchronous Redis client. keys (Iterable[str]): Keys to retrieve from Redis. - concurrency (Optional[int], optional): The number of concurrent - requests to make. + batch_size (Optional[int], optional): Number of objects to write + in a single Redis pipeline execution. Defaults to class's + default batch size. Returns: Dict[str, Any]: Dictionary with keys and their corresponding objects. """ + results: List = [] + if not isinstance(keys, Iterable): # type: ignore raise TypeError("Keys must be an iterable of strings") if len(keys) == 0: # type: ignore return [] - if not concurrency: - concurrency = self.default_write_concurrency - - semaphore = asyncio.Semaphore(concurrency) + if batch_size is None: + batch_size = self.default_batch_size - async def _get(key: str) -> Dict[str, Any]: - async with semaphore: - result = await self._aget(redis_client, key) - return result + # Use a pipeline to batch the retrieval + async with redis_client.pipeline(transaction=False) as pipe: + for i, key in enumerate(keys, start=1): + await self._aget(pipe, key) + if i % batch_size == 0: + results.extend(await pipe.execute()) + if i % batch_size != 0: + results.extend(await pipe.execute()) - tasks = [asyncio.create_task(_get(key)) for key in keys] - results = await asyncio.gather(*tasks) + # Process results return convert_bytes(results) @@ -392,19 +464,6 @@ class HashStorage(BaseStorage): type: IndexType = IndexType.HASH """Hash data type for the index""" - def _validate(self, obj: Dict[str, Any]): - """Validate that the given object is a dictionary, suitable for storage - as a Redis hash. - - Args: - obj (Dict[str, Any]): The object to validate. - - Raises: - TypeError: If the object is not a dictionary. - """ - if not isinstance(obj, dict): - raise TypeError("Object must be a dictionary.") - @staticmethod def _set(client: Redis, key: str, obj: Dict[str, Any]): """Synchronously set a hash value in Redis for the given key. @@ -465,19 +524,6 @@ class JsonStorage(BaseStorage): type: IndexType = IndexType.JSON """JSON data type for the index""" - def _validate(self, obj: Dict[str, Any]): - """Validate that the given object is a dictionary, suitable for JSON - serialization. - - Args: - obj (Dict[str, Any]): The object to validate. - - Raises: - TypeError: If the object is not a dictionary. - """ - if not isinstance(obj, dict): - raise TypeError("Object must be a dictionary.") - @staticmethod def _set(client: Redis, key: str, obj: Dict[str, Any]): """Synchronously set a JSON obj in Redis for the given key. diff --git a/redisvl/query/__init__.py b/redisvl/query/__init__.py index 8246794f..30d35562 100644 --- a/redisvl/query/__init__.py +++ b/redisvl/query/__init__.py @@ -1,17 +1,24 @@ +from redisvl.query.aggregate import AggregationQuery, HybridQuery from redisvl.query.query import ( BaseQuery, + BaseVectorQuery, CountQuery, FilterQuery, RangeQuery, + TextQuery, VectorQuery, VectorRangeQuery, ) __all__ = [ "BaseQuery", + "BaseVectorQuery", "VectorQuery", "FilterQuery", "RangeQuery", "VectorRangeQuery", "CountQuery", + "TextQuery", + "AggregationQuery", + "HybridQuery", ] diff --git a/redisvl/query/aggregate.py b/redisvl/query/aggregate.py new file mode 100644 index 00000000..4e6f4085 --- /dev/null +++ b/redisvl/query/aggregate.py @@ -0,0 +1,220 @@ +from typing import Any, Dict, List, Optional, Set, Tuple, Union + +import nltk +from nltk.corpus import stopwords as nltk_stopwords +from redis.commands.search.aggregation import AggregateRequest, Desc + +from redisvl.query.filter import FilterExpression +from redisvl.redis.utils import array_to_buffer +from redisvl.utils.token_escaper import TokenEscaper + + +class AggregationQuery(AggregateRequest): + """ + Base class for aggregation queries used to create aggregation queries for Redis. + """ + + def __init__(self, query_string): + super().__init__(query_string) + + +class HybridQuery(AggregationQuery): + """ + HybridQuery combines text and vector search in Redis. + It allows you to perform a hybrid search using both text and vector similarity. + It scores documents based on a weighted combination of text and vector similarity. + + .. code-block:: python + + from redisvl.query import HybridQuery + from redisvl.index import SearchIndex + + index = SearchIndex.from_yaml("path/to/index.yaml") + + query = HybridQuery( + text="example text", + text_field_name="text_field", + vector=[0.1, 0.2, 0.3], + vector_field_name="vector_field", + text_scorer="BM25STD", + filter_expression=None, + alpha=0.7, + dtype="float32", + num_results=10, + return_fields=["field1", "field2"], + stopwords="english", + dialect=2, + ) + + results = index.query(query) + + """ + + DISTANCE_ID: str = "vector_distance" + VECTOR_PARAM: str = "vector" + + def __init__( + self, + text: str, + text_field_name: str, + vector: Union[bytes, List[float]], + vector_field_name: str, + text_scorer: str = "BM25STD", + filter_expression: Optional[Union[str, FilterExpression]] = None, + alpha: float = 0.7, + dtype: str = "float32", + num_results: int = 10, + return_fields: Optional[List[str]] = None, + stopwords: Optional[Union[str, Set[str]]] = "english", + dialect: int = 2, + ): + """ + Instantiates a HybridQuery object. + + Args: + text (str): The text to search for. + text_field_name (str): The text field name to search in. + vector (Union[bytes, List[float]]): The vector to perform vector similarity search. + vector_field_name (str): The vector field name to search in. + text_scorer (str, optional): The text scorer to use. Options are {TFIDF, TFIDF.DOCNORM, + BM25, DISMAX, DOCSCORE, BM25STD}. Defaults to "BM25STD". + filter_expression (Optional[FilterExpression], optional): The filter expression to use. + Defaults to None. + alpha (float, optional): The weight of the vector similarity. Documents will be scored + as: hybrid_score = (alpha) * vector_score + (1-alpha) * text_score. + Defaults to 0.7. + dtype (str, optional): The data type of the vector. Defaults to "float32". + num_results (int, optional): The number of results to return. Defaults to 10. + return_fields (Optional[List[str]], optional): The fields to return. Defaults to None. + stopwords (Optional[Union[str, Set[str]]], optional): The stopwords to remove from the + provided text prior to searchuse. If a string such as "english" "german" is + provided then a default set of stopwords for that language will be used. if a list, + set, or tuple of strings is provided then those will be used as stopwords. + Defaults to "english". if set to "None" then no stopwords will be removed. + dialect (int, optional): The Redis dialect version. Defaults to 2. + + Raises: + ValueError: If the text string is empty, or if the text string becomes empty after + stopwords are removed. + TypeError: If the stopwords are not a set, list, or tuple of strings. + """ + + if not text.strip(): + raise ValueError("text string cannot be empty") + + self._text = text + self._text_field = text_field_name + self._vector = vector + self._vector_field = vector_field_name + self._filter_expression = filter_expression + self._alpha = alpha + self._dtype = dtype + self._num_results = num_results + self._set_stopwords(stopwords) + + query_string = self._build_query_string() + super().__init__(query_string) + + self.scorer(text_scorer) # type: ignore[attr-defined] + self.add_scores() # type: ignore[attr-defined] + self.apply( + vector_similarity=f"(2 - @{self.DISTANCE_ID})/2", text_score="@__score" + ) + self.apply(hybrid_score=f"{1-alpha}*@text_score + {alpha}*@vector_similarity") + self.sort_by(Desc("@hybrid_score"), max=num_results) + self.dialect(dialect) # type: ignore[attr-defined] + if return_fields: + self.load(*return_fields) + + @property + def params(self) -> Dict[str, Any]: + """Return the parameters for the aggregation. + + Returns: + Dict[str, Any]: The parameters for the aggregation. + """ + if isinstance(self._vector, bytes): + vector = self._vector + else: + vector = array_to_buffer(self._vector, dtype=self._dtype) + + params = {self.VECTOR_PARAM: vector} + + return params + + @property + def stopwords(self) -> Set[str]: + """Return the stopwords used in the query. + Returns: + Set[str]: The stopwords used in the query. + """ + return self._stopwords.copy() if self._stopwords else set() + + def _set_stopwords(self, stopwords: Optional[Union[str, Set[str]]] = "english"): + """Set the stopwords to use in the query. + Args: + stopwords (Optional[Union[str, Set[str]]]): The stopwords to use. If a string + such as "english" "german" is provided then a default set of stopwords for that + language will be used. if a list, set, or tuple of strings is provided then those + will be used as stopwords. Defaults to "english". if set to "None" then no stopwords + will be removed. + Raises: + TypeError: If the stopwords are not a set, list, or tuple of strings. + """ + if not stopwords: + self._stopwords = set() + elif isinstance(stopwords, str): + try: + nltk.download("stopwords", quiet=True) + self._stopwords = set(nltk_stopwords.words(stopwords)) + except Exception as e: + raise ValueError(f"Error trying to load {stopwords} from nltk. {e}") + elif isinstance(stopwords, (Set, List, Tuple)) and all( # type: ignore + isinstance(word, str) for word in stopwords + ): + self._stopwords = set(stopwords) + else: + raise TypeError("stopwords must be a set, list, or tuple of strings") + + def _tokenize_and_escape_query(self, user_query: str) -> str: + """Convert a raw user query to a redis full text query joined by ORs + Args: + user_query (str): The user query to tokenize and escape. + + Returns: + str: The tokenized and escaped query string. + Raises: + ValueError: If the text string becomes empty after stopwords are removed. + """ + escaper = TokenEscaper() + + tokens = [ + escaper.escape( + token.strip().strip(",").replace("“", "").replace("”", "").lower() + ) + for token in user_query.split() + ] + tokenized = " | ".join( + [token for token in tokens if token and token not in self._stopwords] + ) + + if not tokenized: + raise ValueError("text string cannot be empty after removing stopwords") + return tokenized + + def _build_query_string(self) -> str: + """Build the full query string for text search with optional filtering.""" + if isinstance(self._filter_expression, FilterExpression): + filter_expression = str(self._filter_expression) + else: + filter_expression = "" + + # base KNN query + knn_query = f"KNN {self._num_results} @{self._vector_field} ${self.VECTOR_PARAM} AS {self.DISTANCE_ID}" + + text = f"(~@{self._text_field}:({self._tokenize_and_escape_query(self._text)})" + + if filter_expression and filter_expression != "*": + text += f" AND {filter_expression}" + + return f"{text})=>[{knn_query}]" diff --git a/redisvl/query/filter.py b/redisvl/query/filter.py index 1e8987ff..ced52520 100644 --- a/redisvl/query/filter.py +++ b/redisvl/query/filter.py @@ -1,3 +1,5 @@ +import datetime +import re from enum import Enum from functools import wraps from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union @@ -8,6 +10,19 @@ # mypy: disable-error-code="override" +class Inclusive(str, Enum): + """Enum for valid inclusive options""" + + BOTH = "both" + """Inclusive of both sides of range (default)""" + NEITHER = "neither" + """Inclusive of neither side of range""" + LEFT = "left" + """Inclusive of only left""" + RIGHT = "right" + """Inclusive of only right""" + + class FilterOperator(Enum): EQ = 1 NE = 2 @@ -19,6 +34,7 @@ class FilterOperator(Enum): AND = 8 LIKE = 9 IN = 10 + BETWEEN = 11 class FilterField: @@ -267,6 +283,7 @@ class Num(FilterField): FilterOperator.GT: ">", FilterOperator.LE: "<=", FilterOperator.GE: ">=", + FilterOperator.BETWEEN: "between", } OPERATOR_MAP: Dict[FilterOperator, str] = { FilterOperator.EQ: "@%s:[%s %s]", @@ -275,8 +292,10 @@ class Num(FilterField): FilterOperator.LT: "@%s:[-inf (%s]", FilterOperator.GE: "@%s:[%s +inf]", FilterOperator.LE: "@%s:[-inf %s]", + FilterOperator.BETWEEN: "@%s:[%s %s]", } - SUPPORTED_VAL_TYPES = (int, float, type(None)) + + SUPPORTED_VAL_TYPES = (int, float, tuple, type(None)) def __eq__(self, other: int) -> "FilterExpression": """Create a Numeric equality filter expression. @@ -373,10 +392,51 @@ def __le__(self, other: int) -> "FilterExpression": self._set_value(other, self.SUPPORTED_VAL_TYPES, FilterOperator.LE) return FilterExpression(str(self)) + @staticmethod + def _validate_inclusive_string(inclusive: str) -> Inclusive: + try: + return Inclusive(inclusive) + except: + raise ValueError( + f"Invalid inclusive value must be: {[i.value for i in Inclusive]}" + ) + + def _format_inclusive_between( + self, inclusive: Inclusive, start: int, end: int + ) -> str: + if inclusive.value == Inclusive.BOTH.value: + return f"@{self._field}:[{start} {end}]" + + if inclusive.value == Inclusive.NEITHER.value: + return f"@{self._field}:[({start} ({end}]" + + if inclusive.value == Inclusive.LEFT.value: + return f"@{self._field}:[{start} ({end}]" + + if inclusive.value == Inclusive.RIGHT.value: + return f"@{self._field}:[({start} {end}]" + + raise ValueError(f"Inclusive value not found") + + def between( + self, start: int, end: int, inclusive: str = "both" + ) -> "FilterExpression": + """Operator for searching values between two numeric values.""" + inclusive = self._validate_inclusive_string(inclusive) + expression = self._format_inclusive_between(inclusive, start, end) + + return FilterExpression(expression) + def __str__(self) -> str: """Return the Redis Query string for the Numeric filter""" if self._value is None: return "*" + if self._operator == FilterOperator.BETWEEN: + return self.OPERATOR_MAP[self._operator] % ( + self._field, + self._value[0], + self._value[1], + ) if self._operator == FilterOperator.EQ or self._operator == FilterOperator.NE: return self.OPERATOR_MAP[self._operator] % ( self._field, @@ -562,3 +622,213 @@ def __str__(self) -> str: if not self._filter: raise ValueError("Improperly initialized FilterExpression") return self._filter + + +class Timestamp(Num): + """ + A timestamp filter for querying date/time fields in Redis. + + This filter can handle various date and time formats, including: + - datetime objects (with or without timezone) + - date objects + - ISO-8601 formatted strings + - Unix timestamps (as integers or floats) + + All timestamps are converted to Unix timestamps in UTC for consistency. + """ + + SUPPORTED_TYPES = ( + datetime.datetime, + datetime.date, + tuple, # Date range + str, # ISO format + int, # Unix timestamp + float, # Unix timestamp with fractional seconds + type(None), + ) + + @staticmethod + def _is_date(value: Any) -> bool: + """Check if the value is a date object. Either ISO string or datetime.date.""" + return ( + isinstance(value, datetime.date) + and not isinstance(value, datetime.datetime) + ) or (isinstance(value, str) and Timestamp._is_date_only(value)) + + @staticmethod + def _is_date_only(iso_string: str) -> bool: + """Check if an ISO formatted string only includes date information using regex.""" + # Match YYYY-MM-DD format exactly + date_pattern = r"^\d{4}-\d{2}-\d{2}$" + return bool(re.match(date_pattern, iso_string)) + + def _convert_to_timestamp(self, value, end_date=False): + """ + Convert various inputs to a Unix timestamp (seconds since epoch in UTC). + + Args: + value: A datetime, date, string, int, or float + + Returns: + float: Unix timestamp + """ + if value is None: + return None + + if isinstance(value, (int, float)): + # Already a Unix timestamp + return float(value) + + if isinstance(value, str): + # Parse ISO format + try: + value = datetime.datetime.fromisoformat(value) + except ValueError: + raise ValueError(f"String timestamp must be in ISO format: {value}") + + if isinstance(value, datetime.date) and not isinstance( + value, datetime.datetime + ): + # Convert to max or min if for dates based on end or not + if end_date: + value = datetime.datetime.combine(value, datetime.time.max) + else: + value = datetime.datetime.combine(value, datetime.time.min) + + # Ensure the datetime is timezone-aware (UTC) + if isinstance(value, datetime.datetime): + if value.tzinfo is None: + value = value.replace(tzinfo=datetime.timezone.utc) + else: + value = value.astimezone(datetime.timezone.utc) + + # Convert to Unix timestamp + return value.timestamp() + + raise TypeError(f"Unsupported type for timestamp conversion: {type(value)}") + + def __eq__(self, other) -> FilterExpression: + """ + Filter for timestamps equal to the specified value. + For date objects (without time), this matches the entire day. + + Args: + other: A datetime, date, ISO string, or Unix timestamp + + Returns: + self: The filter object for method chaining + """ + if self._is_date(other): + # For date objects, match the entire day + if isinstance(other, str): + other = datetime.datetime.strptime(other, "%Y-%m-%d").date() + start = datetime.datetime.combine(other, datetime.time.min).astimezone( + datetime.timezone.utc + ) + end = datetime.datetime.combine(other, datetime.time.max).astimezone( + datetime.timezone.utc + ) + return self.between(start, end) + + timestamp = self._convert_to_timestamp(other) + self._set_value(timestamp, self.SUPPORTED_TYPES, FilterOperator.EQ) + return FilterExpression(str(self)) + + def __ne__(self, other) -> FilterExpression: + """ + Filter for timestamps not equal to the specified value. + For date objects (without time), this excludes the entire day. + + Args: + other: A datetime, date, ISO string, or Unix timestamp + + Returns: + self: The filter object for method chaining + """ + if self._is_date(other): + # For date objects, exclude the entire day + if isinstance(other, str): + other = datetime.datetime.strptime(other, "%Y-%m-%d").date() + start = datetime.datetime.combine(other, datetime.time.min) + end = datetime.datetime.combine(other, datetime.time.max) + return self.between(start, end) + + timestamp = self._convert_to_timestamp(other) + self._set_value(timestamp, self.SUPPORTED_TYPES, FilterOperator.NE) + return FilterExpression(str(self)) + + def __gt__(self, other): + """ + Filter for timestamps greater than the specified value. + + Args: + other: A datetime, date, ISO string, or Unix timestamp + + Returns: + self: The filter object for method chaining + """ + timestamp = self._convert_to_timestamp(other) + self._set_value(timestamp, self.SUPPORTED_TYPES, FilterOperator.GT) + return FilterExpression(str(self)) + + def __lt__(self, other): + """ + Filter for timestamps less than the specified value. + + Args: + other: A datetime, date, ISO string, or Unix timestamp + + Returns: + self: The filter object for method chaining + """ + timestamp = self._convert_to_timestamp(other) + self._set_value(timestamp, self.SUPPORTED_TYPES, FilterOperator.LT) + return FilterExpression(str(self)) + + def __ge__(self, other): + """ + Filter for timestamps greater than or equal to the specified value. + + Args: + other: A datetime, date, ISO string, or Unix timestamp + + Returns: + self: The filter object for method chaining + """ + timestamp = self._convert_to_timestamp(other) + self._set_value(timestamp, self.SUPPORTED_TYPES, FilterOperator.GE) + return FilterExpression(str(self)) + + def __le__(self, other): + """ + Filter for timestamps less than or equal to the specified value. + + Args: + other: A datetime, date, ISO string, or Unix timestamp + + Returns: + self: The filter object for method chaining + """ + timestamp = self._convert_to_timestamp(other) + self._set_value(timestamp, self.SUPPORTED_TYPES, FilterOperator.LE) + return FilterExpression(str(self)) + + def between(self, start, end, inclusive: str = "both"): + """ + Filter for timestamps between start and end (inclusive). + + Args: + start: A datetime, date, ISO string, or Unix timestamp + end: A datetime, date, ISO string, or Unix timestamp + + Returns: + self: The filter object for method chaining + """ + inclusive = self._validate_inclusive_string(inclusive) + + start_ts = self._convert_to_timestamp(start) + end_ts = self._convert_to_timestamp(end, end_date=True) + + expression = self._format_inclusive_between(inclusive, start_ts, end_ts) + + return FilterExpression(expression) diff --git a/redisvl/query/query.py b/redisvl/query/query.py index d99a080e..ea09bc19 100644 --- a/redisvl/query/query.py +++ b/redisvl/query/query.py @@ -1,9 +1,14 @@ -from typing import Any, Dict, List, Optional, Union +from enum import Enum +from typing import Any, Dict, List, Optional, Set, Tuple, Union +import nltk +from nltk.corpus import stopwords as nltk_stopwords from redis.commands.search.query import Query as RedisQuery from redisvl.query.filter import FilterExpression from redisvl.redis.utils import array_to_buffer +from redisvl.utils.token_escaper import TokenEscaper +from redisvl.utils.utils import denorm_cosine_distance class BaseQuery(RedisQuery): @@ -90,7 +95,8 @@ def __init__( num_results (Optional[int], optional): The number of results to return. Defaults to 10. dialect (int, optional): The query dialect. Defaults to 2. sort_by (Optional[str], optional): The field to order the results by. Defaults to None. - in_order (bool, optional): Requires the terms in the field to have the same order as the terms in the query filter. Defaults to False. + in_order (bool, optional): Requires the terms in the field to have the same order as the + terms in the query filter. Defaults to False. params (Optional[Dict[str, Any]], optional): The parameters for the query. Defaults to None. Raises: @@ -135,7 +141,8 @@ def __init__( """A query for a simple count operation provided some filter expression. Args: - filter_expression (Optional[Union[str, FilterExpression]]): The filter expression to query with. Defaults to None. + filter_expression (Optional[Union[str, FilterExpression]]): The filter expression to + query with. Defaults to None. params (Optional[Dict[str, Any]], optional): The parameters for the query. Defaults to None. Raises: @@ -174,6 +181,15 @@ class BaseVectorQuery: DISTANCE_ID: str = "vector_distance" VECTOR_PARAM: str = "vector" + _normalize_vector_distance: bool = False + + +class HybridPolicy(str, Enum): + """Enum for valid hybrid policy options in vector queries.""" + + BATCHES = "BATCHES" + ADHOC_BF = "ADHOC_BF" + class VectorQuery(BaseVectorQuery, BaseQuery): def __init__( @@ -188,6 +204,9 @@ def __init__( dialect: int = 2, sort_by: Optional[str] = None, in_order: bool = False, + hybrid_policy: Optional[str] = None, + batch_size: Optional[int] = None, + normalize_vector_distance: bool = False, ): """A query for running a vector search along with an optional filter expression. @@ -204,6 +223,7 @@ def __init__( "float32". num_results (int, optional): The top k results to return from the vector search. Defaults to 10. + return_score (bool, optional): Whether to return the vector distance. Defaults to True. dialect (int, optional): The RediSearch query dialect. @@ -213,6 +233,22 @@ def __init__( in_order (bool): Requires the terms in the field to have the same order as the terms in the query filter, regardless of the offsets between them. Defaults to False. + hybrid_policy (Optional[str]): Controls how filters are applied during vector search. + Options are "BATCHES" (paginates through small batches of nearest neighbors) or + "ADHOC_BF" (computes scores for all vectors passing the filter). + "BATCHES" mode is typically faster for queries with selective filters. + "ADHOC_BF" mode is better when filters match a large portion of the dataset. + Defaults to None, which lets Redis auto-select the optimal policy. + batch_size (Optional[int]): When hybrid_policy is "BATCHES", controls the number + of vectors to fetch in each batch. Larger values may improve performance + at the cost of memory usage. Only applies when hybrid_policy="BATCHES". + Defaults to None, which lets Redis auto-select an appropriate batch size. + normalize_vector_distance (bool): Redis supports 3 distance metrics: L2 (euclidean), + IP (inner product), and COSINE. By default, L2 distance returns an unbounded value. + COSINE distance returns a value between 0 and 2. IP returns a value determined by + the magnitude of the vector. Setting this flag to true converts COSINE and L2 distance + to a similarity score between 0 and 1. Note: setting this flag to true for IP will + throw a warning since by definition COSINE similarity is normalized IP. Raises: TypeError: If filter_expression is not of type redisvl.query.FilterExpression @@ -224,6 +260,9 @@ def __init__( self._vector_field_name = vector_field_name self._dtype = dtype self._num_results = num_results + self._hybrid_policy: Optional[HybridPolicy] = None + self._batch_size: Optional[int] = None + self._normalize_vector_distance = normalize_vector_distance self.set_filter(filter_expression) query_string = self._build_query_string() @@ -246,12 +285,92 @@ def __init__( if in_order: self.in_order() + if hybrid_policy is not None: + self.set_hybrid_policy(hybrid_policy) + + if batch_size is not None: + self.set_batch_size(batch_size) + def _build_query_string(self) -> str: """Build the full query string for vector search with optional filtering.""" filter_expression = self._filter_expression if isinstance(filter_expression, FilterExpression): filter_expression = str(filter_expression) - return f"{filter_expression}=>[KNN {self._num_results} @{self._vector_field_name} ${self.VECTOR_PARAM} AS {self.DISTANCE_ID}]" + + # Base KNN query + knn_query = ( + f"KNN {self._num_results} @{self._vector_field_name} ${self.VECTOR_PARAM}" + ) + + # Add hybrid policy parameters if specified + if self._hybrid_policy: + knn_query += f" HYBRID_POLICY {self._hybrid_policy.value}" + + # Add batch size if specified and using BATCHES policy + if self._hybrid_policy == HybridPolicy.BATCHES and self._batch_size: + knn_query += f" BATCH_SIZE {self._batch_size}" + + # Add distance field alias + knn_query += f" AS {self.DISTANCE_ID}" + + return f"{filter_expression}=>[{knn_query}]" + + def set_hybrid_policy(self, hybrid_policy: str): + """Set the hybrid policy for the query. + + Args: + hybrid_policy (str): The hybrid policy to use. Options are "BATCHES" + or "ADHOC_BF". + + Raises: + ValueError: If hybrid_policy is not one of the valid options + """ + try: + self._hybrid_policy = HybridPolicy(hybrid_policy) + except ValueError: + raise ValueError( + f"hybrid_policy must be one of {', '.join([p.value for p in HybridPolicy])}" + ) + + # Reset the query string + self._query_string = self._build_query_string() + + def set_batch_size(self, batch_size: int): + """Set the batch size for the query. + + Args: + batch_size (int): The batch size to use when hybrid_policy is "BATCHES". + + Raises: + TypeError: If batch_size is not an integer + ValueError: If batch_size is not positive + """ + if not isinstance(batch_size, int): + raise TypeError("batch_size must be an integer") + if batch_size <= 0: + raise ValueError("batch_size must be positive") + self._batch_size = batch_size + + # Reset the query string + self._query_string = self._build_query_string() + + @property + def hybrid_policy(self) -> Optional[str]: + """Return the hybrid policy for the query. + + Returns: + Optional[str]: The hybrid policy for the query. + """ + return self._hybrid_policy.value if self._hybrid_policy else None + + @property + def batch_size(self) -> Optional[int]: + """Return the batch size for the query. + + Returns: + Optional[int]: The batch size for the query. + """ + return self._batch_size @property def params(self) -> Dict[str, Any]: @@ -265,11 +384,16 @@ def params(self) -> Dict[str, Any]: else: vector = array_to_buffer(self._vector, dtype=self._dtype) - return {self.VECTOR_PARAM: vector} + params = {self.VECTOR_PARAM: vector} + + return params class VectorRangeQuery(BaseVectorQuery, BaseQuery): DISTANCE_THRESHOLD_PARAM: str = "distance_threshold" + EPSILON_PARAM: str = "EPSILON" # Parameter name for epsilon + HYBRID_POLICY_PARAM: str = "HYBRID_POLICY" # Parameter name for hybrid policy + BATCH_SIZE_PARAM: str = "BATCH_SIZE" # Parameter name for batch size def __init__( self, @@ -279,11 +403,15 @@ def __init__( filter_expression: Optional[Union[str, FilterExpression]] = None, dtype: str = "float32", distance_threshold: float = 0.2, + epsilon: Optional[float] = None, num_results: int = 10, return_score: bool = True, dialect: int = 2, sort_by: Optional[str] = None, in_order: bool = False, + hybrid_policy: Optional[str] = None, + batch_size: Optional[int] = None, + normalize_vector_distance: bool = False, ): """A query for running a filtered vector search based on semantic distance threshold. @@ -298,9 +426,14 @@ def __init__( along with the range query. Defaults to None. dtype (str, optional): The dtype of the vector. Defaults to "float32". - distance_threshold (str, float): The threshold for vector distance. + distance_threshold (float): The threshold for vector distance. A smaller threshold indicates a stricter semantic search. Defaults to 0.2. + epsilon (Optional[float]): The relative factor for vector range queries, + setting boundaries for candidates within radius * (1 + epsilon). + This controls how extensive the search is beyond the specified radius. + Higher values increase recall at the expense of performance. + Defaults to None, which uses the index-defined epsilon (typically 0.01). num_results (int): The MAX number of results to return. Defaults to 10. return_score (bool, optional): Whether to return the vector @@ -312,6 +445,22 @@ def __init__( in_order (bool): Requires the terms in the field to have the same order as the terms in the query filter, regardless of the offsets between them. Defaults to False. + hybrid_policy (Optional[str]): Controls how filters are applied during vector search. + Options are "BATCHES" (paginates through small batches of nearest neighbors) or + "ADHOC_BF" (computes scores for all vectors passing the filter). + "BATCHES" mode is typically faster for queries with selective filters. + "ADHOC_BF" mode is better when filters match a large portion of the dataset. + Defaults to None, which lets Redis auto-select the optimal policy. + batch_size (Optional[int]): When hybrid_policy is "BATCHES", controls the number + of vectors to fetch in each batch. Larger values may improve performance + at the cost of memory usage. Only applies when hybrid_policy="BATCHES". + Defaults to None, which lets Redis auto-select an appropriate batch size. + normalize_vector_distance (bool): Redis supports 3 distance metrics: L2 (euclidean), + IP (inner product), and COSINE. By default, L2 distance returns an unbounded value. + COSINE distance returns a value between 0 and 2. IP returns a value determined by + the magnitude of the vector. Setting this flag to true converts COSINE and L2 distance + to a similarity score between 0 and 1. Note: setting this flag to true for IP will + throw a warning since by definition COSINE similarity is normalized IP. Raises: TypeError: If filter_expression is not of type redisvl.query.FilterExpression @@ -324,6 +473,21 @@ def __init__( self._vector_field_name = vector_field_name self._dtype = dtype self._num_results = num_results + self._distance_threshold: float = 0.2 # Initialize with default + self._epsilon: Optional[float] = None + self._hybrid_policy: Optional[HybridPolicy] = None + self._batch_size: Optional[int] = None + + if epsilon is not None: + self.set_epsilon(epsilon) + + if hybrid_policy is not None: + self.set_hybrid_policy(hybrid_policy) + + if batch_size is not None: + self.set_batch_size(batch_size) + + self._normalize_vector_distance = normalize_vector_distance self.set_distance_threshold(distance_threshold) self.set_filter(filter_expression) query_string = self._build_query_string() @@ -347,27 +511,115 @@ def __init__( if in_order: self.in_order() + def set_distance_threshold(self, distance_threshold: float): + """Set the distance threshold for the query. + + Args: + distance_threshold (float): Vector distance threshold. + + Raises: + TypeError: If distance_threshold is not a float or int + ValueError: If distance_threshold is negative + """ + if not isinstance(distance_threshold, (float, int)): + raise TypeError("distance_threshold must be of type float or int") + if distance_threshold < 0: + raise ValueError("distance_threshold must be non-negative") + if self._normalize_vector_distance: + if distance_threshold > 1: + raise ValueError( + "distance_threshold must be between 0 and 1 when normalize_vector_distance is set to True" + ) + + # User sets normalized value 0-1 denormalize for use in DB + distance_threshold = denorm_cosine_distance(distance_threshold) + self._distance_threshold = distance_threshold + + # Reset the query string + self._query_string = self._build_query_string() + + def set_epsilon(self, epsilon: float): + """Set the epsilon parameter for the range query. + + Args: + epsilon (float): The relative factor for vector range queries, + setting boundaries for candidates within radius * (1 + epsilon). + + Raises: + TypeError: If epsilon is not a float or int + ValueError: If epsilon is negative + """ + if not isinstance(epsilon, (float, int)): + raise TypeError("epsilon must be of type float or int") + if epsilon < 0: + raise ValueError("epsilon must be non-negative") + self._epsilon = epsilon + + # Reset the query string + self._query_string = self._build_query_string() + + def set_hybrid_policy(self, hybrid_policy: str): + """Set the hybrid policy for the query. + + Args: + hybrid_policy (str): The hybrid policy to use. Options are "BATCHES" + or "ADHOC_BF". + + Raises: + ValueError: If hybrid_policy is not one of the valid options + """ + try: + self._hybrid_policy = HybridPolicy(hybrid_policy) + except ValueError: + raise ValueError( + f"hybrid_policy must be one of {', '.join([p.value for p in HybridPolicy])}" + ) + + # Reset the query string + self._query_string = self._build_query_string() + + def set_batch_size(self, batch_size: int): + """Set the batch size for the query. + + Args: + batch_size (int): The batch size to use when hybrid_policy is "BATCHES". + + Raises: + TypeError: If batch_size is not an integer + ValueError: If batch_size is not positive + """ + if not isinstance(batch_size, int): + raise TypeError("batch_size must be an integer") + if batch_size <= 0: + raise ValueError("batch_size must be positive") + self._batch_size = batch_size + + # Reset the query string + self._query_string = self._build_query_string() + def _build_query_string(self) -> str: """Build the full query string for vector range queries with optional filtering""" + # Build base query with vector range only base_query = f"@{self._vector_field_name}:[VECTOR_RANGE ${self.DISTANCE_THRESHOLD_PARAM} ${self.VECTOR_PARAM}]" + # Build query attributes section + attr_parts = [] + attr_parts.append(f"$YIELD_DISTANCE_AS: {self.DISTANCE_ID}") + + if self._epsilon is not None: + attr_parts.append(f"$EPSILON: {self._epsilon}") + + # Add query attributes section + attr_section = f"=>{{{'; '.join(attr_parts)}}}" + + # Add filter expression if present filter_expression = self._filter_expression if isinstance(filter_expression, FilterExpression): filter_expression = str(filter_expression) if filter_expression == "*": - return f"{base_query}=>{{$yield_distance_as: {self.DISTANCE_ID}}}" - return f"({base_query}=>{{$yield_distance_as: {self.DISTANCE_ID}}} {filter_expression})" - - def set_distance_threshold(self, distance_threshold: float): - """Set the distance threshold for the query. - - Args: - distance_threshold (float): vector distance - """ - if not isinstance(distance_threshold, (float, int)): - raise TypeError("distance_threshold must be of type int or float") - self._distance_threshold = distance_threshold + return f"{base_query}{attr_section}" + return f"({base_query}{attr_section} {filter_expression})" @property def distance_threshold(self) -> float: @@ -378,6 +630,33 @@ def distance_threshold(self) -> float: """ return self._distance_threshold + @property + def epsilon(self) -> Optional[float]: + """Return the epsilon for the query. + + Returns: + Optional[float]: The epsilon for the query, or None if not set. + """ + return self._epsilon + + @property + def hybrid_policy(self) -> Optional[str]: + """Return the hybrid policy for the query. + + Returns: + Optional[str]: The hybrid policy for the query. + """ + return self._hybrid_policy.value if self._hybrid_policy else None + + @property + def batch_size(self) -> Optional[int]: + """Return the batch size for the query. + + Returns: + Optional[int]: The batch size for the query. + """ + return self._batch_size + @property def params(self) -> Dict[str, Any]: """Return the parameters for the query. @@ -390,12 +669,192 @@ def params(self) -> Dict[str, Any]: else: vector_param = array_to_buffer(self._vector, dtype=self._dtype) - return { + params = { self.VECTOR_PARAM: vector_param, self.DISTANCE_THRESHOLD_PARAM: self._distance_threshold, } + # Add hybrid policy and batch size as query parameters (not in query string) + if self._hybrid_policy: + params[self.HYBRID_POLICY_PARAM] = self._hybrid_policy.value + + if self._hybrid_policy == HybridPolicy.BATCHES and self._batch_size: + params[self.BATCH_SIZE_PARAM] = self._batch_size + + return params + class RangeQuery(VectorRangeQuery): # keep for backwards compatibility pass + + +class TextQuery(BaseQuery): + """ + TextQuery is a query for running a full text search, along with an optional filter expression. + + .. code-block:: python + + from redisvl.query import TextQuery + from redisvl.index import SearchIndex + + index = SearchIndex.from_yaml(index.yaml) + + query = TextQuery( + text="example text", + text_field_name="text_field", + text_scorer="BM25STD", + filter_expression=None, + num_results=10, + return_fields=["field1", "field2"], + stopwords="english", + dialect=2, + ) + + results = index.query(query) + """ + + def __init__( + self, + text: str, + text_field_name: str, + text_scorer: str = "BM25STD", + filter_expression: Optional[Union[str, FilterExpression]] = None, + return_fields: Optional[List[str]] = None, + num_results: int = 10, + return_score: bool = True, + dialect: int = 2, + sort_by: Optional[str] = None, + in_order: bool = False, + params: Optional[Dict[str, Any]] = None, + stopwords: Optional[Union[str, Set[str]]] = "english", + ): + """A query for running a full text search, along with an optional filter expression. + + Args: + text (str): The text string to perform the text search with. + text_field_name (str): The name of the document field to perform text search on. + text_scorer (str, optional): The text scoring algorithm to use. + Defaults to BM25STD. Options are {TFIDF, BM25STD, BM25, TFIDF.DOCNORM, DISMAX, DOCSCORE}. + See https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/scoring/ + filter_expression (Union[str, FilterExpression], optional): A filter to apply + along with the text search. Defaults to None. + return_fields (List[str]): The declared fields to return with search + results. + num_results (int, optional): The top k results to return from the + search. Defaults to 10. + return_score (bool, optional): Whether to return the text score. + Defaults to True. + dialect (int, optional): The RediSearch query dialect. + Defaults to 2. + sort_by (Optional[str]): The field to order the results by. Defaults + to None. Results will be ordered by text score. + in_order (bool): Requires the terms in the field to have + the same order as the terms in the query filter, regardless of + the offsets between them. Defaults to False. + params (Optional[Dict[str, Any]], optional): The parameters for the query. + Defaults to None. + stopwords (Optional[Union[str, Set[str]]): The set of stop words to remove + from the query text. If a language like 'english' or 'spanish' is provided + a default set of stopwords for that language will be used. Users may specify + their own stop words by providing a List or Set of words. if set to None, + then no words will be removed. Defaults to 'english'. + + Raises: + ValueError: if stopwords language string cannot be loaded. + TypeError: If stopwords is not a valid iterable set of strings. + """ + self._text = text + self._text_field = text_field_name + self._num_results = num_results + + self._set_stopwords(stopwords) + self.set_filter(filter_expression) + + if params: + self._params = params + + # initialize the base query with the full query string and filter expression + query_string = self._build_query_string() + super().__init__(query_string) + + # handle query settings + self.scorer(text_scorer) + + if return_fields: + self.return_fields(*return_fields) + self.paging(0, self._num_results).dialect(dialect) + + if sort_by: + self.sort_by(sort_by) + + if in_order: + self.in_order() + + if return_score: + self.with_scores() + + @property + def stopwords(self): + return self._stopwords + + def _set_stopwords(self, stopwords: Optional[Union[str, Set[str]]] = "english"): + """Set the stopwords to use in the query. + Args: + stopwords (Optional[Union[str, Set[str]]]): The stopwords to use. If a string + such as "english" "german" is provided then a default set of stopwords for that + language will be used. if a list, set, or tuple of strings is provided then those + will be used as stopwords. Defaults to "english". if set to "None" then no stopwords + will be removed. + Raises: + TypeError: If the stopwords are not a set, list, or tuple of strings. + """ + if not stopwords: + self._stopwords = set() + elif isinstance(stopwords, str): + try: + nltk.download("stopwords", quiet=True) + self._stopwords = set(nltk_stopwords.words(stopwords)) + except Exception as e: + raise ValueError(f"Error trying to load {stopwords} from nltk. {e}") + elif isinstance(stopwords, (Set, List, Tuple)) and all( # type: ignore + isinstance(word, str) for word in stopwords + ): + self._stopwords = set(stopwords) + else: + raise TypeError("stopwords must be a set, list, or tuple of strings") + + def _tokenize_and_escape_query(self, user_query: str) -> str: + """Convert a raw user query to a redis full text query joined by ORs + Args: + user_query (str): The user query to tokenize and escape. + + Returns: + str: The tokenized and escaped query string. + Raises: + ValueError: If the text string becomes empty after stopwords are removed. + """ + escaper = TokenEscaper() + + tokens = [ + escaper.escape( + token.strip().strip(",").replace("“", "").replace("”", "").lower() + ) + for token in user_query.split() + ] + return " | ".join( + [token for token in tokens if token and token not in self._stopwords] + ) + + def _build_query_string(self) -> str: + """Build the full query string for text search with optional filtering.""" + filter_expression = self._filter_expression + if isinstance(filter_expression, FilterExpression): + filter_expression = str(filter_expression) + else: + filter_expression = "" + + text = f"@{self._text_field}:({self._tokenize_and_escape_query(self._text)})" + if filter_expression and filter_expression != "*": + text += f" AND {filter_expression}" + return text diff --git a/redisvl/schema/__init__.py b/redisvl/schema/__init__.py index 24f6b821..c835ccd5 100644 --- a/redisvl/schema/__init__.py +++ b/redisvl/schema/__init__.py @@ -1,3 +1,35 @@ +from redisvl.schema.fields import ( + BaseField, + FieldTypes, + FlatVectorField, + GeoField, + HNSWVectorField, + NumericField, + TagField, + TextField, + VectorDataType, + VectorDistanceMetric, + VectorIndexAlgorithm, +) from redisvl.schema.schema import IndexInfo, IndexSchema, StorageType -__all__ = ["StorageType", "IndexSchema", "IndexInfo"] +# Expose validation functionality +from redisvl.schema.validation import validate_object + +__all__ = [ + "IndexSchema", + "IndexInfo", + "StorageType", + "FieldTypes", + "VectorDistanceMetric", + "VectorDataType", + "VectorIndexAlgorithm", + "BaseField", + "TextField", + "TagField", + "NumericField", + "GeoField", + "FlatVectorField", + "HNSWVectorField", + "validate_object", +] diff --git a/redisvl/schema/fields.py b/redisvl/schema/fields.py index 17714480..1df294d8 100644 --- a/redisvl/schema/fields.py +++ b/redisvl/schema/fields.py @@ -16,6 +16,14 @@ from redis.commands.search.field import TextField as RedisTextField from redis.commands.search.field import VectorField as RedisVectorField +from redisvl.utils.utils import norm_cosine_distance, norm_l2_distance + +VECTOR_NORM_MAP = { + "COSINE": norm_cosine_distance, + "L2": norm_l2_distance, + "IP": None, # normalized inner product is cosine similarity by definition +} + class FieldTypes(str, Enum): TAG = "tag" @@ -164,12 +172,14 @@ class BaseField(BaseModel): """Specified field attributes""" def _handle_names(self) -> Tuple[str, Optional[str]]: + """Helper to handle field naming with path support""" if self.path: return self.path, self.name return self.name, None def as_redis_field(self) -> RedisField: - raise NotImplementedError + """Convert schema field to Redis Field object""" + raise NotImplementedError("Must be implemented by field subclasses") class TextField(BaseField): diff --git a/redisvl/schema/schema.py b/redisvl/schema/schema.py index 33dfd9c7..90617d18 100644 --- a/redisvl/schema/schema.py +++ b/redisvl/schema/schema.py @@ -8,6 +8,7 @@ from redis.commands.search.field import Field as RedisField from redisvl.schema.fields import BaseField, FieldFactory +from redisvl.schema.type_utils import TypeInferrer from redisvl.utils.log import get_logger from redisvl.utils.utils import model_to_dict @@ -455,64 +456,3 @@ def to_yaml(self, file_path: str, overwrite: bool = True) -> None: with open(fp, "w") as f: yaml_data = self.to_dict() yaml.dump(yaml_data, f, sort_keys=False) - - -class TypeInferrer: - """Infers the type of a field based on its value.""" - - GEO_PATTERN = re.compile( - r"^\s*[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)\s*$" - ) - - TYPE_METHOD_MAP = { - "numeric": "_is_numeric", - "geo": "_is_geographic", - "tag": "_is_tag", - "text": "_is_text", - } - - @classmethod - def infer(cls, value: Any) -> str: - """Infers the field type for a given value. - - Args: - value: The value to infer the type of. - - Returns: - The inferred field type as a string. - - Raises: - ValueError: If the type cannot be inferred. - """ - for type_name, method_name in cls.TYPE_METHOD_MAP.items(): - if getattr(cls, method_name)(value): - return type_name - raise ValueError(f"Unable to infer type for value: {value}") - - @classmethod - def _is_numeric(cls, value: Any) -> bool: - """Check if the value is numeric.""" - if not isinstance(value, (int, float, str)): - return False - try: - float(value) - return True - except (ValueError, TypeError): - return False - - @classmethod - def _is_tag(cls, value: Any) -> bool: - """Check if the value is a tag.""" - return isinstance(value, (list, set, tuple)) and all( - isinstance(v, str) for v in value - ) - - @classmethod - def _is_text(cls, value: Any) -> bool: - """Check if the value is text.""" - return isinstance(value, str) - - @classmethod - def _is_geographic(cls, value: Any) -> bool: - """Check if the value is a geographic coordinate.""" - return isinstance(value, str) and cls.GEO_PATTERN.match(value) is not None diff --git a/redisvl/schema/type_utils.py b/redisvl/schema/type_utils.py new file mode 100644 index 00000000..83329961 --- /dev/null +++ b/redisvl/schema/type_utils.py @@ -0,0 +1,63 @@ +import re +from typing import Any + + +class TypeInferrer: + """Infers the type of a field based on its value.""" + + GEO_PATTERN = re.compile( + r"^\s*[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)\s*$" + ) + + TYPE_METHOD_MAP = { + "numeric": "_is_numeric", + "geo": "_is_geographic", + "tag": "_is_tag", + "text": "_is_text", + } + + @classmethod + def infer(cls, value: Any) -> str: + """Infers the field type for a given value. + + Args: + value: The value to infer the type of. + + Returns: + The inferred field type as a string. + + Raises: + ValueError: If the type cannot be inferred. + """ + for type_name, method_name in cls.TYPE_METHOD_MAP.items(): + if getattr(cls, method_name)(value): + return type_name + raise ValueError(f"Unable to infer type for value: {value}") + + @classmethod + def _is_numeric(cls, value: Any) -> bool: + """Check if the value is numeric.""" + if not isinstance(value, (int, float, str)): + return False + try: + float(value) + return True + except (ValueError, TypeError): + return False + + @classmethod + def _is_tag(cls, value: Any) -> bool: + """Check if the value is a tag.""" + return isinstance(value, (list, set, tuple)) and all( + isinstance(v, str) for v in value + ) + + @classmethod + def _is_text(cls, value: Any) -> bool: + """Check if the value is text.""" + return isinstance(value, str) + + @classmethod + def _is_geographic(cls, value: Any) -> bool: + """Check if the value is a geographic coordinate.""" + return isinstance(value, str) and cls.GEO_PATTERN.match(value) is not None diff --git a/redisvl/schema/validation.py b/redisvl/schema/validation.py new file mode 100644 index 00000000..629b193b --- /dev/null +++ b/redisvl/schema/validation.py @@ -0,0 +1,277 @@ +""" +RedisVL Schema Validation Module + +This module provides utilities for validating data against RedisVL schemas +using dynamically generated Pydantic models. +""" + +import json +from typing import Any, Dict, List, Optional, Type, Union + +from jsonpath_ng import parse as jsonpath_parse +from pydantic import BaseModel, Field, field_validator + +from redisvl.schema import IndexSchema +from redisvl.schema.fields import BaseField, FieldTypes, VectorDataType +from redisvl.schema.schema import StorageType +from redisvl.schema.type_utils import TypeInferrer +from redisvl.utils.log import get_logger + +logger = get_logger(__name__) + + +class SchemaModelGenerator: + """ + Generates and caches Pydantic models based on Redis schema definitions. + + This class handles the conversion of RedisVL IndexSchema objects into + Pydantic models with appropriate field types and validators. + """ + + _model_cache: Dict[str, Type[BaseModel]] = {} + + @classmethod + def get_model_for_schema(cls, schema: IndexSchema) -> Type[BaseModel]: + """ + Get or create a Pydantic model for a schema. + + Args: + schema: The IndexSchema to convert to a Pydantic model + + Returns: + A Pydantic model class that can validate data against the schema + """ + # Use schema identifier as cache key + cache_key = str(hash(json.dumps(schema.to_dict(), sort_keys=True).encode())) + + if cache_key not in cls._model_cache: + cls._model_cache[cache_key] = cls._create_model(schema) + + return cls._model_cache[cache_key] + + @classmethod + def _map_field_to_pydantic_type( + cls, field: BaseField, storage_type: StorageType + ) -> Type[Any]: + """ + Map Redis field types to appropriate Pydantic types. + + Args: + field: The Redis field definition + storage_type: The storage type (HASH or JSON) + + Returns: + The Pydantic field type + + Raises: + ValueError: If the field type is not supported + """ + if field.type == FieldTypes.TEXT: + return str + elif field.type == FieldTypes.TAG: + return str + elif field.type == FieldTypes.NUMERIC: + return Union[int, float] # type: ignore + elif field.type == FieldTypes.GEO: + return str + elif field.type == FieldTypes.VECTOR: + # For JSON storage, vectors are always lists + if storage_type == StorageType.JSON: + # For int data types, vectors must be ints, otherwise floats + if field.attrs.datatype in ( # type: ignore + VectorDataType.INT8, + VectorDataType.UINT8, + ): + return List[int] + return List[float] + else: + return bytes + + # If we get here, the field type is not supported + raise ValueError(f"Unsupported field type: {field.type}") + + @classmethod + def _create_model(cls, schema: IndexSchema) -> Type[BaseModel]: + """ + Create a Pydantic model from schema definition using type() approach. + + Args: + schema: The IndexSchema to convert + + Returns: + A Pydantic model class with appropriate fields and validators + """ + # Get storage type from schema + storage_type = schema.index.storage_type + + # Create annotations dictionary for the dynamic model + annotations: Dict[str, Any] = {} + class_dict: Dict[str, Any] = {} + + # Build annotations and field metadata + for field_name, field in schema.fields.items(): + field_type = cls._map_field_to_pydantic_type(field, storage_type) + + # Make all fields optional in the model + annotations[field_name] = Optional[field_type] + + # Add default=None to make fields truly optional (can be missing from input) + class_dict[field_name] = Field(default=None) + + # Register validators for GEO fields + if field.type == FieldTypes.GEO: + + def make_geo_validator(fname: str): + @field_validator(fname, mode="after") + def _validate_geo(cls, value): + # Skip validation for None values + if value is not None: + # Validate against pattern + if not TypeInferrer._is_geographic(value): + raise ValueError( + f"Geo field '{fname}' value '{value}' is not a valid 'lat,lon' format" + ) + return value + + return _validate_geo + + class_dict[f"validate_{field_name}"] = make_geo_validator(field_name) + + # Register validators for NUMERIC fields + elif field.type == FieldTypes.NUMERIC: + + def make_numeric_validator(fname: str): + # mode='before' so it catches bools before parsing + @field_validator(fname, mode="before") + def _disallow_bool(cls, value): + if isinstance(value, bool): + raise ValueError(f"Field '{fname}' cannot be boolean.") + return value + + return _disallow_bool + + class_dict[f"validate_{field_name}"] = make_numeric_validator( + field_name + ) + + # Register validators for VECTOR fields + elif field.type == FieldTypes.VECTOR: + dims = field.attrs.dims # type: ignore + datatype = field.attrs.datatype # type: ignore + + def make_vector_validator( + fname: str, dims: int, datatype: VectorDataType + ): + @field_validator(fname, mode="after") + def _validate_vector(cls, value): + # Skip validation for None values + if value is not None: + # Handle list representation + if isinstance(value, list): + # Validate dimensions + if len(value) != dims: + raise ValueError( + f"Vector field '{fname}' must have {dims} dimensions, got {len(value)}" + ) + # Validate data types + datatype_str = str(datatype).upper() + # Integer-based datatypes + if datatype_str in ("INT8", "UINT8"): + # Check range for INT8 + if datatype_str == "INT8": + if any(v < -128 or v > 127 for v in value): + raise ValueError( + f"Vector field '{fname}' contains values outside the INT8 range (-128 to 127)" + ) + # Check range for UINT8 + elif datatype_str == "UINT8": + if any(v < 0 or v > 255 for v in value): + raise ValueError( + f"Vector field '{fname}' contains values outside the UINT8 range (0 to 255)" + ) + return value + + return _validate_vector + + class_dict[f"validate_{field_name}"] = make_vector_validator( + field_name, dims, datatype + ) + + # Create class dictionary with annotations and field metadata + class_dict.update( + **{ + "__annotations__": annotations, + "model_config": {"arbitrary_types_allowed": True, "extra": "allow"}, + } + ) + + # Create the model class using type() + model_name = f"{schema.index.name}__PydanticModel" + return type(model_name, (BaseModel,), class_dict) + + +def extract_from_json_path(obj: Dict[str, Any], path: str) -> Any: + """ + Extract a value from a nested JSON object using a JSONPath expression. + + Args: + obj: The object to extract values from + path: JSONPath expression (e.g., $.field.subfield, $.[*].name) + + Returns: + The extracted value or None if not found + + Notes: + This function uses the jsonpath-ng library for proper JSONPath parsing + and supports the full JSONPath specification including filters, wildcards, + and array indexing. + """ + # If path doesn't start with $, add it as per JSONPath spec + if not path.startswith("$"): + path = f"$.{path}" + + # Parse and find the JSONPath expression + jsonpath_expr = jsonpath_parse(path) + matches = jsonpath_expr.find(obj) + + # Return the first match value, or None if no matches + if matches: + return matches[0].value + return None + + +def validate_object(schema: IndexSchema, obj: Dict[str, Any]) -> Dict[str, Any]: + """ + Validate an object against a schema. + + Args: + schema: The IndexSchema to validate against + obj: The object to validate + + Returns: + Validated object with any type coercions applied + + Raises: + ValueError: If validation fails with enhanced error message + """ + # Get Pydantic model for this schema + model_class = SchemaModelGenerator.get_model_for_schema(schema) + + # Prepare object for validation + # Handle nested JSON if needed + if schema.index.storage_type == StorageType.JSON: + # Extract values from nested paths + flat_obj = {} + for field_name, field in schema.fields.items(): + if field.path: + value = extract_from_json_path(obj, field.path) + if value is not None: + flat_obj[field_name] = value + elif field_name in obj: + flat_obj[field_name] = obj[field_name] + else: + flat_obj = obj + + # Validate against model + validated = model_class.model_validate(flat_obj) + return validated.model_dump(exclude_none=True) diff --git a/redisvl/utils/log.py b/redisvl/utils/log.py index 41d5fcb1..a71f33e2 100644 --- a/redisvl/utils/log.py +++ b/redisvl/utils/log.py @@ -3,7 +3,6 @@ import coloredlogs -# constants for logging coloredlogs.DEFAULT_DATE_FORMAT = "%H:%M:%S" coloredlogs.DEFAULT_LOG_FORMAT = "%(asctime)s %(name)s %(levelname)s %(message)s" @@ -15,5 +14,16 @@ def get_logger(name, log_level="info", fmt=None): name = "RedisVL" if log_level == "debug" else name logger = logging.getLogger(name) - coloredlogs.install(level=log_level, logger=logger, fmt=fmt, stream=sys.stdout) + + # Only configure this specific logger, not the root logger + # Check if the logger already has handlers to respect existing configuration + if not logger.handlers: + coloredlogs.install( + level=log_level, + logger=logger, # Pass the specific logger + fmt=fmt, + stream=sys.stdout, + isatty=True, # Only use colors when supported + reconfigure=False, # Don't reconfigure existing loggers + ) return logger diff --git a/redisvl/utils/optimize/__init__.py b/redisvl/utils/optimize/__init__.py new file mode 100644 index 00000000..8025228f --- /dev/null +++ b/redisvl/utils/optimize/__init__.py @@ -0,0 +1,12 @@ +from redisvl.utils.optimize.base import BaseThresholdOptimizer, EvalMetric +from redisvl.utils.optimize.cache import CacheThresholdOptimizer +from redisvl.utils.optimize.router import RouterThresholdOptimizer +from redisvl.utils.optimize.schema import LabeledData + +__all__ = [ + "CacheThresholdOptimizer", + "RouterThresholdOptimizer", + "EvalMetric", + "BaseThresholdOptimizer", + "LabeledData", +] diff --git a/redisvl/utils/optimize/base.py b/redisvl/utils/optimize/base.py new file mode 100644 index 00000000..daa8be60 --- /dev/null +++ b/redisvl/utils/optimize/base.py @@ -0,0 +1,45 @@ +from abc import ABC, abstractmethod +from enum import Enum +from typing import Any, Callable, Dict, List, TypeVar + +from redisvl.utils.optimize.utils import _validate_test_dict + + +class EvalMetric(str, Enum): + """Evaluation metrics for threshold optimization.""" + + F1 = "f1" + PRECISION = "precision" + RECALL = "recall" + + +T = TypeVar("T") # Type variable for the optimizable object (Cache or Router) + + +class BaseThresholdOptimizer(ABC): + """Abstract base class for threshold optimizers.""" + + def __init__( + self, + optimizable: T, + test_dict: List[Dict], + opt_fn: Callable, + eval_metric: str = "f1", + ): + """Initialize the optimizer. + + Args: + optimizable: The object to optimize (Cache or Router) + test_dict: List of test cases + eval_fn: Function to evaluate performance + opt_fn: Function to perform optimization + """ + self.test_data = _validate_test_dict(test_dict) + self.optimizable = optimizable + self.eval_metric = EvalMetric(eval_metric) + self.opt_fn = opt_fn + + @abstractmethod + def optimize(self, **kwargs: Any): + """Optimize thresholds using the provided optimization function.""" + pass diff --git a/redisvl/utils/optimize/cache.py b/redisvl/utils/optimize/cache.py new file mode 100644 index 00000000..05c99f76 --- /dev/null +++ b/redisvl/utils/optimize/cache.py @@ -0,0 +1,134 @@ +from typing import Any, Callable, Dict, List + +import numpy as np +from ranx import Qrels, Run, evaluate + +from redisvl.extensions.llmcache.semantic import SemanticCache +from redisvl.query import RangeQuery +from redisvl.utils.optimize.base import BaseThresholdOptimizer, EvalMetric +from redisvl.utils.optimize.schema import LabeledData +from redisvl.utils.optimize.utils import NULL_RESPONSE_KEY, _format_qrels + + +def _generate_run_cache(test_data: List[LabeledData], threshold: float) -> Run: + """Format observed data for evaluation with ranx""" + run_dict: Dict[str, Dict[str, int]] = {} + + for td in test_data: + run_dict[td.id] = {} + for res in td.response: + if float(res["vector_distance"]) < threshold: + # value of 1 is irrelevant checks only on match for f1 + run_dict[td.id][res["id"]] = 1 + + if not run_dict[td.id]: + # ranx is a little odd in that if there are no matches it errors + # if however there are no keys that match you get the correct score + run_dict[td.id][NULL_RESPONSE_KEY] = 1 + + return Run(run_dict) + + +def _eval_cache( + test_data: List[LabeledData], threshold: float, qrels: Qrels, metric: str +) -> float: + """Formats run data and evaluates supported metric""" + run = _generate_run_cache(test_data, threshold) + return evaluate(qrels, run, metric, make_comparable=True) + + +def _get_best_threshold(metrics: dict) -> float: + """ + Returns the threshold with the highest F1 score. + If multiple thresholds have the same F1 score, returns the lowest threshold. + """ + return max(metrics.items(), key=lambda x: (x[1]["score"], -x[0]))[0] + + +def _grid_search_opt_cache( + cache: SemanticCache, test_data: List[LabeledData], eval_metric: EvalMetric +): + """Evaluates all thresholds in linspace for cache to determine optimal""" + thresholds = np.linspace(0.01, 0.8, 60) + metrics = {} + + for td in test_data: + vec = cache._vectorizer.embed(td.query) + query = RangeQuery( + vec, vector_field_name="prompt_vector", distance_threshold=1.0 + ) + res = cache.index.query(query) + td.response = res + + qrels = _format_qrels(test_data) + + for threshold in thresholds: + score = _eval_cache(test_data, threshold, qrels, eval_metric.value) + metrics[threshold] = {"score": score} + + best_threshold = _get_best_threshold(metrics) + cache.set_threshold(best_threshold) + + +class CacheThresholdOptimizer(BaseThresholdOptimizer): + """ + Class for optimizing thresholds for a SemanticCache. + + .. code-block:: python + + from redisvl.extensions.llmcache import SemanticCache + from redisvl.utils.optimize import CacheThresholdOptimizer + + sem_cache = SemanticCache( + name="sem_cache", # underlying search index name + redis_url="redis://localhost:6379", # redis connection url string + distance_threshold=0.5 # semantic cache distance threshold + ) + + paris_key = sem_cache.store(prompt="what is the capital of france?", response="paris") + rabat_key = sem_cache.store(prompt="what is the capital of morocco?", response="rabat") + + test_data = [ + { + "query": "What's the capital of Britain?", + "query_match": "" + }, + { + "query": "What's the capital of France??", + "query_match": paris_key + }, + { + "query": "What's the capital city of Morocco?", + "query_match": rabat_key + }, + ] + + optimizer = CacheThresholdOptimizer(sem_cache, test_data) + optimizer.optimize() + """ + + def __init__( + self, + cache: SemanticCache, + test_dict: List[Dict[str, Any]], + opt_fn: Callable = _grid_search_opt_cache, + eval_metric: str = "f1", + ): + """Initialize the cache optimizer. + + Args: + cache (SemanticCache): The RedisVL SemanticCache instance to optimize. + test_dict (List[Dict[str, Any]]): List of test cases. + opt_fn (Callable): Function to perform optimization. Defaults to + grid search. + eval_metric (str): Evaluation metric for threshold optimization. + Defaults to "f1" score. + + Raises: + ValueError: If the test_dict not in LabeledData format. + """ + super().__init__(cache, test_dict, opt_fn, eval_metric) + + def optimize(self, **kwargs: Any): + """Optimize thresholds using the provided optimization function for cache case.""" + self.opt_fn(self.optimizable, self.test_data, self.eval_metric, **kwargs) diff --git a/redisvl/utils/optimize/router.py b/redisvl/utils/optimize/router.py new file mode 100644 index 00000000..b384e127 --- /dev/null +++ b/redisvl/utils/optimize/router.py @@ -0,0 +1,159 @@ +import random +from typing import Any, Callable, Dict, List + +import numpy as np +from ranx import Qrels, Run, evaluate + +from redisvl.extensions.router.semantic import SemanticRouter +from redisvl.utils.optimize.base import BaseThresholdOptimizer, EvalMetric +from redisvl.utils.optimize.schema import LabeledData +from redisvl.utils.optimize.utils import NULL_RESPONSE_KEY, _format_qrels + + +def _generate_run_router(test_data: List[LabeledData], router: SemanticRouter) -> Run: + """Format router results into format for ranx Run""" + run_dict: Dict[Any, Any] = {} + + for td in test_data: + run_dict[td.id] = {} + route_match = router(td.query) + if route_match and route_match.name == td.query_match: + run_dict[td.id][td.query_match] = 1 + else: + run_dict[td.id][NULL_RESPONSE_KEY] = 1 + + return Run(run_dict) + + +def _eval_router( + router: SemanticRouter, test_data: List[LabeledData], qrels: Qrels, eval_metric: str +) -> float: + """Evaluate acceptable metric given run and qrels data""" + run = _generate_run_router(test_data, router) + return evaluate(qrels, run, eval_metric, make_comparable=True) + + +def _router_random_search( + route_names: List[str], route_thresholds: dict, search_step=0.10 +): + """Performs random search for many thresholds to many routes""" + score_threshold_values = [] + for route in route_names: + score_threshold_values.append( + np.linspace( + start=max(route_thresholds[route] - search_step, 0), + stop=route_thresholds[route] + search_step, + num=100, + ) + ) + + return { + route: float(random.choice(score_threshold_values[i])) + for i, route in enumerate(route_names) + } + + +def _random_search_opt_router( + router: SemanticRouter, + test_data: List[LabeledData], + qrels: Qrels, + eval_metric: EvalMetric, + **kwargs: Any, +): + """Performs complete optimization for router cases provide acceptable metric""" + + start_score = _eval_router(router, test_data, qrels, eval_metric.value) + best_score = start_score + best_thresholds = router.route_thresholds + + max_iterations = kwargs.get("max_iterations", 20) + search_step = kwargs.get("search_step", 0.10) + + for _ in range(max_iterations): + route_names = router.route_names + route_thresholds = router.route_thresholds + thresholds = _router_random_search( + route_names=route_names, + route_thresholds=route_thresholds, + search_step=search_step, + ) + router.update_route_thresholds(thresholds) + score = _eval_router(router, test_data, qrels, eval_metric.value) + if score > best_score: + best_score = score + best_thresholds = thresholds + + print( + f"Eval metric {eval_metric.value.upper()}: start {round(start_score, 3)}, end {round(best_score, 3)} \nEnding thresholds: {router.route_thresholds}" + ) + router.update_route_thresholds(best_thresholds) + + +class RouterThresholdOptimizer(BaseThresholdOptimizer): + """ + Class for optimizing thresholds for a SemanticRouter. + + .. code-block:: python + + from redisvl.extensions.router import Route, SemanticRouter + from redisvl.utils.vectorize import HFTextVectorizer + from redisvl.utils.optimize import RouterThresholdOptimizer + + routes = [ + Route( + name="greeting", + references=["hello", "hi"], + metadata={"type": "greeting"}, + distance_threshold=0.5, + ), + Route( + name="farewell", + references=["bye", "goodbye"], + metadata={"type": "farewell"}, + distance_threshold=0.5, + ), + ] + + router = SemanticRouter( + name="greeting-router", + vectorizer=HFTextVectorizer(), + routes=routes, + redis_url="redis://localhost:6379", + overwrite=True # Blow away any other routing index with this name + ) + + test_data = [ + {"query": "hello", "query_match": "greeting"}, + {"query": "goodbye", "query_match": "farewell"}, + ... + ] + + optimizer = RouterThresholdOptimizer(router, test_data) + optimizer.optimize() + """ + + def __init__( + self, + router: SemanticRouter, + test_dict: List[Dict[str, Any]], + opt_fn: Callable = _random_search_opt_router, + eval_metric: str = "f1", + ): + """Initialize the router optimizer. + + Args: + router (SemanticRouter): The RedisVL SemanticRouter instance to optimize. + test_dict (List[Dict[str, Any]]): List of test cases. + opt_fn (Callable): Function to perform optimization. Defaults to + grid search. + eval_metric (str): Evaluation metric for threshold optimization. + Defaults to "f1" score. + Raises: + ValueError: If the test_dict not in LabeledData format. + """ + super().__init__(router, test_dict, opt_fn, eval_metric) + + def optimize(self, **kwargs: Any): + """Optimize kicks off the optimization process for router""" + qrels = _format_qrels(self.test_data) + self.opt_fn(self.optimizable, self.test_data, qrels, self.eval_metric, **kwargs) diff --git a/redisvl/utils/optimize/schema.py b/redisvl/utils/optimize/schema.py new file mode 100644 index 00000000..f71d10f6 --- /dev/null +++ b/redisvl/utils/optimize/schema.py @@ -0,0 +1,11 @@ +from typing import List, Optional + +from pydantic import BaseModel, Field +from ulid import ULID + + +class LabeledData(BaseModel): + id: str = Field(default_factory=lambda: str(ULID())) + query: str + query_match: Optional[str] + response: List[dict] = [] diff --git a/redisvl/utils/optimize/utils.py b/redisvl/utils/optimize/utils.py new file mode 100644 index 00000000..bebc1c79 --- /dev/null +++ b/redisvl/utils/optimize/utils.py @@ -0,0 +1,26 @@ +from typing import List + +from ranx import Qrels + +from redisvl.utils.optimize.schema import LabeledData + +NULL_RESPONSE_KEY = "no_match" + + +def _format_qrels(test_data: List[LabeledData]) -> Qrels: + """Utility function for creating qrels for evaluation with ranx""" + qrels_dict = {} + + for td in test_data: + if td.query_match: + qrels_dict[td.id] = {td.query_match: 1} + else: + # This is for capturing true negatives from test set + qrels_dict[td.id] = {NULL_RESPONSE_KEY: 1} + + return Qrels(qrels_dict) + + +def _validate_test_dict(test_dict: List[dict]) -> List[LabeledData]: + """Convert/validate test_dict for use in optimizer""" + return [LabeledData(**d) for d in test_dict] diff --git a/redisvl/utils/utils.py b/redisvl/utils/utils.py index 4c40d41a..ab13f16f 100644 --- a/redisvl/utils/utils.py +++ b/redisvl/utils/utils.py @@ -191,3 +191,25 @@ def wrapper(): return return wrapper + + +def norm_cosine_distance(value: float) -> float: + """ + Normalize a cosine distance to a similarity score between 0 and 1. + """ + return max((2 - value) / 2, 0) + + +def denorm_cosine_distance(value: float) -> float: + """ + Denormalize a similarity score between 0 and 1 to a cosine distance between + 0 and 2. + """ + return max(2 - 2 * value, 0) + + +def norm_l2_distance(value: float) -> float: + """ + Normalize the L2 distance. + """ + return 1 / (1 + value) diff --git a/redisvl/version.py b/redisvl/version.py index 3d26edf7..3d187266 100644 --- a/redisvl/version.py +++ b/redisvl/version.py @@ -1 +1 @@ -__version__ = "0.4.1" +__version__ = "0.5.0" diff --git a/schemas/semantic_router.yaml b/schemas/semantic_router.yaml index 0f5bab19..d3e24f85 100644 --- a/schemas/semantic_router.yaml +++ b/schemas/semantic_router.yaml @@ -13,7 +13,7 @@ routes: - goodbye metadata: type: farewell - distance_threshold: 0.3 + distance_threshold: 0.2 vectorizer: type: hf model: sentence-transformers/all-mpnet-base-v2 diff --git a/scripts.py b/scripts.py index 77020aad..31e9a390 100644 --- a/scripts.py +++ b/scripts.py @@ -39,7 +39,16 @@ def test(): def test_verbose(): - test_cmd = ["python", "-m", "pytest", "-n", "auto", "-vv", "-s", "--log-level=CRITICAL"] + test_cmd = [ + "python", + "-m", + "pytest", + "-n", + "auto", + "-vv", + "-s", + "--log-level=CRITICAL", + ] # Get any extra arguments passed to the script extra_args = sys.argv[1:] if extra_args: @@ -48,7 +57,22 @@ def test_verbose(): def test_notebooks(): - subprocess.run("cd docs/ && python -m pytest --nbval-lax ./user_guide -vv", shell=True, check=True) + test_cmd = [ + "python", + "-m", + "pytest", + "--nbval-lax", + "./docs/user_guide", + "-vvv", + ] + extra_args = sys.argv[1:] + if extra_args: + test_cmd.extend(extra_args) + + subprocess.run( + test_cmd, + check=True, + ) def build_docs(): diff --git a/tests/conftest.py b/tests/conftest.py index 61c5de45..de6b901a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,11 @@ import os +from datetime import datetime, timezone import pytest from testcontainers.compose import DockerCompose from redisvl.redis.connection import RedisConnectionFactory +from redisvl.utils.vectorize import HFTextVectorizer @pytest.fixture(autouse=True) @@ -67,13 +69,33 @@ def client(redis_url): yield conn +@pytest.fixture(scope="session", autouse=True) +def hf_vectorizer(): + return HFTextVectorizer( + model="sentence-transformers/all-mpnet-base-v2", + token=os.getenv("HF_TOKEN"), + cache_folder=os.getenv("SENTENCE_TRANSFORMERS_HOME"), + ) + + +@pytest.fixture +def sample_datetimes(): + return { + "low": datetime(2025, 1, 16, 13).astimezone(timezone.utc), + "mid": datetime(2025, 2, 16, 13).astimezone(timezone.utc), + "high": datetime(2025, 3, 16, 13).astimezone(timezone.utc), + } + + @pytest.fixture -def sample_data(): +def sample_data(sample_datetimes): return [ { "user": "john", "age": 18, "job": "engineer", + "description": "engineers conduct trains that ride on train tracks", + "last_updated": sample_datetimes["low"].timestamp(), "credit_score": "high", "location": "-122.4194,37.7749", "user_embedding": [0.1, 0.1, 0.5], @@ -82,6 +104,8 @@ def sample_data(): "user": "mary", "age": 14, "job": "doctor", + "description": "a medical professional who treats diseases and helps people stay healthy", + "last_updated": sample_datetimes["low"].timestamp(), "credit_score": "low", "location": "-122.4194,37.7749", "user_embedding": [0.1, 0.1, 0.5], @@ -90,6 +114,8 @@ def sample_data(): "user": "nancy", "age": 94, "job": "doctor", + "description": "a research scientist specializing in cancers and diseases of the lungs", + "last_updated": sample_datetimes["mid"].timestamp(), "credit_score": "high", "location": "-122.4194,37.7749", "user_embedding": [0.7, 0.1, 0.5], @@ -98,6 +124,8 @@ def sample_data(): "user": "tyler", "age": 100, "job": "engineer", + "description": "a software developer with expertise in mathematics and computer science", + "last_updated": sample_datetimes["mid"].timestamp(), "credit_score": "high", "location": "-110.0839,37.3861", "user_embedding": [0.1, 0.4, 0.5], @@ -106,6 +134,8 @@ def sample_data(): "user": "tim", "age": 12, "job": "dermatologist", + "description": "a medical professional specializing in diseases of the skin", + "last_updated": sample_datetimes["mid"].timestamp(), "credit_score": "high", "location": "-110.0839,37.3861", "user_embedding": [0.4, 0.4, 0.5], @@ -114,6 +144,8 @@ def sample_data(): "user": "taimur", "age": 15, "job": "CEO", + "description": "high stress, but financially rewarding position at the head of a company", + "last_updated": sample_datetimes["high"].timestamp(), "credit_score": "low", "location": "-110.0839,37.3861", "user_embedding": [0.6, 0.1, 0.5], @@ -122,9 +154,11 @@ def sample_data(): "user": "joe", "age": 35, "job": "dentist", + "description": "like the tooth fairy because they'll take your teeth, but you have to pay them!", + "last_updated": sample_datetimes["high"].timestamp(), "credit_score": "medium", "location": "-110.0839,37.3861", - "user_embedding": [0.9, 0.9, 0.1], + "user_embedding": [-0.1, -0.1, -0.5], }, ] diff --git a/tests/integration/test_aggregation.py b/tests/integration/test_aggregation.py new file mode 100644 index 00000000..deab8afe --- /dev/null +++ b/tests/integration/test_aggregation.py @@ -0,0 +1,308 @@ +import pytest +from redis.commands.search.aggregation import AggregateResult +from redis.commands.search.result import Result + +from redisvl.index import SearchIndex +from redisvl.query import HybridQuery +from redisvl.query.filter import FilterExpression, Geo, GeoRadius, Num, Tag, Text +from redisvl.redis.connection import compare_versions +from redisvl.redis.utils import array_to_buffer + + +@pytest.fixture +def index(sample_data, redis_url): + index = SearchIndex.from_dict( + { + "index": { + "name": "user_index", + "prefix": "v1", + "storage_type": "hash", + }, + "fields": [ + {"name": "credit_score", "type": "tag"}, + {"name": "job", "type": "text"}, + {"name": "description", "type": "text"}, + {"name": "age", "type": "numeric"}, + {"name": "last_updated", "type": "numeric"}, + {"name": "location", "type": "geo"}, + { + "name": "user_embedding", + "type": "vector", + "attrs": { + "dims": 3, + "distance_metric": "cosine", + "algorithm": "flat", + "datatype": "float32", + }, + }, + ], + }, + redis_url=redis_url, + ) + + # create the index (no data yet) + index.create(overwrite=True) + + # prepare and load the data + def hash_preprocess(item: dict) -> dict: + return { + **item, + "user_embedding": array_to_buffer(item["user_embedding"], "float32"), + } + + index.load(sample_data, preprocess=hash_preprocess) + + # run the test + yield index + + # clean up + index.delete(drop=True) + + +def test_aggregation_query(index): + redis_version = index.client.info()["redis_version"] + if not compare_versions(redis_version, "7.2.0"): + pytest.skip("Not using a late enough version of Redis") + + text = "a medical professional with expertise in lung cancer" + text_field = "description" + vector = [0.1, 0.1, 0.5] + vector_field = "user_embedding" + return_fields = ["user", "credit_score", "age", "job", "location", "description"] + + hybrid_query = HybridQuery( + text=text, + text_field_name=text_field, + vector=vector, + vector_field_name=vector_field, + return_fields=return_fields, + ) + + results = index.query(hybrid_query) + assert isinstance(results, list) + assert len(results) == 7 + for doc in results: + assert doc["user"] in [ + "john", + "derrick", + "nancy", + "tyler", + "tim", + "taimur", + "joe", + "mary", + ] + assert int(doc["age"]) in [18, 14, 94, 100, 12, 15, 35] + assert doc["job"] in ["engineer", "doctor", "dermatologist", "CEO", "dentist"] + assert doc["credit_score"] in ["high", "low", "medium"] + + hybrid_query = HybridQuery( + text=text, + text_field_name=text_field, + vector=vector, + vector_field_name=vector_field, + num_results=3, + ) + + results = index.query(hybrid_query) + assert len(results) == 3 + assert ( + results[0]["hybrid_score"] + >= results[1]["hybrid_score"] + >= results[2]["hybrid_score"] + ) + + +def test_empty_query_string(): + text = "" + text_field = "description" + vector = [0.1, 0.1, 0.5] + vector_field = "user_embedding" + return_fields = ["user", "credit_score", "age", "job", "location", "description"] + + # test if text is empty + with pytest.raises(ValueError): + hybrid_query = HybridQuery( + text=text, + text_field_name=text_field, + vector=vector, + vector_field_name=vector_field, + ) + + # test if text becomes empty after stopwords are removed + text = "with a for but and" # will all be removed as default stopwords + with pytest.raises(ValueError): + hybrid_query = HybridQuery( + text=text, + text_field_name=text_field, + vector=vector, + vector_field_name=vector_field, + ) + + +def test_aggregation_query_with_filter(index): + redis_version = index.client.info()["redis_version"] + if not compare_versions(redis_version, "7.2.0"): + pytest.skip("Not using a late enough version of Redis") + + text = "a medical professional with expertise in lung cancer" + text_field = "description" + vector = [0.1, 0.1, 0.5] + vector_field = "user_embedding" + return_fields = ["user", "credit_score", "age", "job", "location", "description"] + filter_expression = (Tag("credit_score") == ("high")) & (Num("age") > 30) + + hybrid_query = HybridQuery( + text=text, + text_field_name=text_field, + vector=vector, + vector_field_name=vector_field, + filter_expression=filter_expression, + return_fields=return_fields, + ) + + results = index.query(hybrid_query) + assert len(results) == 2 + for result in results: + assert result["credit_score"] == "high" + assert int(result["age"]) > 30 + + +def test_aggregation_query_with_geo_filter(index): + redis_version = index.client.info()["redis_version"] + if not compare_versions(redis_version, "7.2.0"): + pytest.skip("Not using a late enough version of Redis") + + text = "a medical professional with expertise in lung cancer" + text_field = "description" + vector = [0.1, 0.1, 0.5] + vector_field = "user_embedding" + return_fields = ["user", "credit_score", "age", "job", "location", "description"] + filter_expression = Geo("location") == GeoRadius(-122.4194, 37.7749, 1000, "m") + + hybrid_query = HybridQuery( + text=text, + text_field_name=text_field, + vector=vector, + vector_field_name=vector_field, + filter_expression=filter_expression, + return_fields=return_fields, + ) + + results = index.query(hybrid_query) + assert len(results) == 3 + for result in results: + assert result["location"] is not None + + +@pytest.mark.parametrize("alpha", [0.1, 0.5, 0.9]) +def test_aggregate_query_alpha(index, alpha): + redis_version = index.client.info()["redis_version"] + if not compare_versions(redis_version, "7.2.0"): + pytest.skip("Not using a late enough version of Redis") + + text = "a medical professional with expertise in lung cancer" + text_field = "description" + vector = [0.1, 0.1, 0.5] + vector_field = "user_embedding" + + hybrid_query = HybridQuery( + text=text, + text_field_name=text_field, + vector=vector, + vector_field_name=vector_field, + alpha=alpha, + ) + + results = index.query(hybrid_query) + assert len(results) == 7 + for result in results: + score = alpha * float(result["vector_similarity"]) + (1 - alpha) * float( + result["text_score"] + ) + assert ( + float(result["hybrid_score"]) - score <= 0.0001 + ) # allow for small floating point error + + +def test_aggregate_query_stopwords(index): + redis_version = index.client.info()["redis_version"] + if not compare_versions(redis_version, "7.2.0"): + pytest.skip("Not using a late enough version of Redis") + + text = "a medical professional with expertise in lung cancer" + text_field = "description" + vector = [0.1, 0.1, 0.5] + vector_field = "user_embedding" + alpha = 0.5 + + hybrid_query = HybridQuery( + text=text, + text_field_name=text_field, + vector=vector, + vector_field_name=vector_field, + alpha=alpha, + stopwords=["medical", "expertise"], + ) + + query_string = hybrid_query._build_query_string() + + assert "medical" not in query_string + assert "expertize" not in query_string + + results = index.query(hybrid_query) + assert len(results) == 7 + for result in results: + score = alpha * float(result["vector_similarity"]) + (1 - alpha) * float( + result["text_score"] + ) + assert ( + float(result["hybrid_score"]) - score <= 0.0001 + ) # allow for small floating point error + + +def test_aggregate_query_with_text_filter(index): + redis_version = index.client.info()["redis_version"] + if not compare_versions(redis_version, "7.2.0"): + pytest.skip("Not using a late enough version of Redis") + + text = "a medical professional with expertise in lung cancer" + text_field = "description" + vector = [0.1, 0.1, 0.5] + vector_field = "user_embedding" + filter_expression = Text(text_field) == ("medical") + + # make sure we can still apply filters to the same text field we are querying + hybrid_query = HybridQuery( + text=text, + text_field_name=text_field, + vector=vector, + vector_field_name=vector_field, + alpha=0.5, + filter_expression=filter_expression, + return_fields=["job", "description"], + ) + + results = index.query(hybrid_query) + assert len(results) == 2 + for result in results: + assert "medical" in result[text_field].lower() + + filter_expression = (Text(text_field) == ("medical")) & ( + (Text(text_field) != ("research")) + ) + hybrid_query = HybridQuery( + text=text, + text_field_name=text_field, + vector=vector, + vector_field_name=vector_field, + alpha=0.5, + filter_expression=filter_expression, + return_fields=["description"], + ) + + results = index.query(hybrid_query) + assert len(results) == 2 + for result in results: + assert "medical" in result[text_field].lower() + assert "research" not in result[text_field].lower() diff --git a/tests/integration/test_async_search_index.py b/tests/integration/test_async_search_index.py index edc6c01a..32a2e3d3 100644 --- a/tests/integration/test_async_search_index.py +++ b/tests/integration/test_async_search_index.py @@ -5,7 +5,7 @@ from redis import Redis as SyncRedis from redis.asyncio import Redis as AsyncRedis -from redisvl.exceptions import RedisModuleVersionError, RedisSearchError +from redisvl.exceptions import RedisModuleVersionError, RedisSearchError, RedisVLError from redisvl.index import AsyncSearchIndex from redisvl.query import VectorQuery from redisvl.query.query import FilterQuery @@ -269,7 +269,7 @@ async def test_search_index_load_preprocess(async_index): await async_index.create(overwrite=True, drop=True) data = [{"id": "1", "test": "foo"}] - async def preprocess(record): + def preprocess(record): record["test"] = "bar" return record @@ -281,10 +281,10 @@ async def preprocess(record): == "bar" ) - async def bad_preprocess(record): + def bad_preprocess(record): return 1 - with pytest.raises(TypeError): + with pytest.raises(RedisVLError): await async_index.load(data, id_field="id", preprocess=bad_preprocess) @@ -300,7 +300,7 @@ async def test_no_id_field(async_index): bad_data = [{"wrong_key": "1", "value": "test"}] # catch missing / invalid id_field - with pytest.raises(ValueError): + with pytest.raises(RedisVLError): await async_index.load(bad_data, id_field="key") diff --git a/tests/unit/test_cross_encoder_reranker.py b/tests/integration/test_cross_encoder_reranker.py similarity index 100% rename from tests/unit/test_cross_encoder_reranker.py rename to tests/integration/test_cross_encoder_reranker.py diff --git a/tests/integration/test_flow_async.py b/tests/integration/test_flow_async.py index a368f677..c727fd28 100644 --- a/tests/integration/test_flow_async.py +++ b/tests/integration/test_flow_async.py @@ -52,7 +52,7 @@ async def test_simple(async_client, schema, sample_data): await index.create(overwrite=True, drop=True) # Prepare and load the data based on storage type - async def hash_preprocess(item: dict) -> dict: + def hash_preprocess(item: dict) -> dict: return { **item, "user_embedding": array_to_buffer(item["user_embedding"], "float32"), diff --git a/tests/integration/test_query.py b/tests/integration/test_query.py index 271d36da..7a0229fe 100644 --- a/tests/integration/test_query.py +++ b/tests/integration/test_query.py @@ -1,9 +1,26 @@ +from datetime import timedelta + import pytest from redis.commands.search.result import Result from redisvl.index import SearchIndex -from redisvl.query import CountQuery, FilterQuery, RangeQuery, VectorQuery -from redisvl.query.filter import FilterExpression, Geo, GeoRadius, Num, Tag, Text +from redisvl.query import ( + CountQuery, + FilterQuery, + TextQuery, + VectorQuery, + VectorRangeQuery, +) +from redisvl.query.filter import ( + FilterExpression, + Geo, + GeoRadius, + Num, + Tag, + Text, + Timestamp, +) +from redisvl.query.query import VectorRangeQuery from redisvl.redis.utils import array_to_buffer # TODO expand to multiple schema types and sync + async @@ -14,7 +31,15 @@ def vector_query(): return VectorQuery( vector=[0.1, 0.1, 0.5], vector_field_name="user_embedding", - return_fields=["user", "credit_score", "age", "job", "location"], + return_score=True, + return_fields=[ + "user", + "credit_score", + "age", + "job", + "location", + "last_updated", + ], ) @@ -23,15 +48,47 @@ def sorted_vector_query(): return VectorQuery( vector=[0.1, 0.1, 0.5], vector_field_name="user_embedding", - return_fields=["user", "credit_score", "age", "job", "location"], + return_fields=[ + "user", + "credit_score", + "age", + "job", + "location", + "last_updated", + ], sort_by="age", ) +@pytest.fixture +def normalized_vector_query(): + return VectorQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + normalize_vector_distance=True, + return_score=True, + return_fields=[ + "user", + "credit_score", + "age", + "job", + "location", + "last_updated", + ], + ) + + @pytest.fixture def filter_query(): return FilterQuery( - return_fields=["user", "credit_score", "age", "job", "location"], + return_fields=[ + "user", + "credit_score", + "age", + "job", + "location", + "last_updated", + ], filter_expression=Tag("credit_score") == "high", ) @@ -39,15 +96,34 @@ def filter_query(): @pytest.fixture def sorted_filter_query(): return FilterQuery( - return_fields=["user", "credit_score", "age", "job", "location"], + return_fields=[ + "user", + "credit_score", + "age", + "job", + "location", + "last_updated", + ], filter_expression=Tag("credit_score") == "high", sort_by="age", ) +@pytest.fixture +def normalized_range_query(): + return VectorRangeQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + normalize_vector_distance=True, + return_score=True, + return_fields=["user", "credit_score", "age", "job", "location"], + distance_threshold=0.2, + ) + + @pytest.fixture def range_query(): - return RangeQuery( + return VectorRangeQuery( vector=[0.1, 0.1, 0.5], vector_field_name="user_embedding", return_fields=["user", "credit_score", "age", "job", "location"], @@ -57,7 +133,7 @@ def range_query(): @pytest.fixture def sorted_range_query(): - return RangeQuery( + return VectorRangeQuery( vector=[0.1, 0.1, 0.5], vector_field_name="user_embedding", return_fields=["user", "credit_score", "age", "job", "location"], @@ -77,9 +153,11 @@ def index(sample_data, redis_url): "storage_type": "hash", }, "fields": [ + {"name": "description", "type": "text"}, {"name": "credit_score", "type": "tag"}, {"name": "job", "type": "text"}, {"name": "age", "type": "numeric"}, + {"name": "last_updated", "type": "numeric"}, {"name": "location", "type": "geo"}, { "name": "user_embedding", @@ -115,6 +193,56 @@ def hash_preprocess(item: dict) -> dict: index.delete(drop=True) +@pytest.fixture +def L2_index(sample_data, redis_url): + # construct a search index from the schema + index = SearchIndex.from_dict( + { + "index": { + "name": "L2_index", + "prefix": "L2_index", + "storage_type": "hash", + }, + "fields": [ + {"name": "credit_score", "type": "tag"}, + {"name": "job", "type": "text"}, + {"name": "age", "type": "numeric"}, + {"name": "last_updated", "type": "numeric"}, + {"name": "location", "type": "geo"}, + { + "name": "user_embedding", + "type": "vector", + "attrs": { + "dims": 3, + "distance_metric": "L2", + "algorithm": "flat", + "datatype": "float32", + }, + }, + ], + }, + redis_url=redis_url, + ) + + # create the index (no data yet) + index.create(overwrite=True) + + # Prepare and load the data + def hash_preprocess(item: dict) -> dict: + return { + **item, + "user_embedding": array_to_buffer(item["user_embedding"], "float32"), + } + + index.load(sample_data, preprocess=hash_preprocess) + + # run the test + yield index + + # clean up + index.delete(drop=True) + + def test_search_and_query(index): # *=>[KNN 7 @user_embedding $vector AS vector_distance] v = VectorQuery( @@ -151,7 +279,7 @@ def test_search_and_query(index): def test_range_query(index): - r = RangeQuery( + r = VectorRangeQuery( vector=[0.1, 0.1, 0.5], vector_field_name="user_embedding", return_fields=["user", "credit_score", "age", "job"], @@ -222,7 +350,7 @@ def search( assert doc.location == location # if range query, test results by distance threshold - if isinstance(query, RangeQuery): + if isinstance(query, VectorRangeQuery): for doc in results.docs: print(doc.vector_distance) assert float(doc.vector_distance) <= distance_threshold @@ -233,7 +361,7 @@ def search( # check results are in sorted order if sort: - if isinstance(query, RangeQuery): + if isinstance(query, VectorRangeQuery): assert [int(doc.age) for doc in results.docs] == [12, 14, 18, 100] else: assert [int(doc.age) for doc in results.docs] == [ @@ -249,13 +377,13 @@ def search( @pytest.fixture( params=["vector_query", "filter_query", "range_query"], - ids=["VectorQuery", "FilterQuery", "RangeQuery"], + ids=["VectorQuery", "FilterQuery", "VectorRangeQuery"], ) def query(request): return request.getfixturevalue(request.param) -def test_filters(index, query): +def test_filters(index, query, sample_datetimes): # Simple Tag Filter t = Tag("credit_score") == "high" search(query, index, t, 4, credit_check="high") @@ -310,6 +438,34 @@ def test_filters(index, query): t = Text("job") % "" search(query, index, t, 7) + # Timestamps + ts = Timestamp("last_updated") > sample_datetimes["mid"] + search(query, index, ts, 2) + + ts = Timestamp("last_updated") >= sample_datetimes["mid"] + search(query, index, ts, 5) + + ts = Timestamp("last_updated") < sample_datetimes["high"] + search(query, index, ts, 5) + + ts = Timestamp("last_updated") <= sample_datetimes["mid"] + search(query, index, ts, 5) + + ts = Timestamp("last_updated") == sample_datetimes["mid"] + search(query, index, ts, 3) + + ts = (Timestamp("last_updated") == sample_datetimes["low"]) | ( + Timestamp("last_updated") == sample_datetimes["high"] + ) + search(query, index, ts, 4) + + # could drop between if we prefer union syntax + ts = Timestamp("last_updated").between( + sample_datetimes["low"] + timedelta(seconds=1), + sample_datetimes["high"] - timedelta(seconds=1), + ) + search(query, index, ts, 3) + def test_manual_string_filters(index, query): # Simple Tag Filter @@ -464,3 +620,264 @@ def test_query_with_chunk_number_zero(): assert ( str(filter_conditions) == expected_query_str ), "Query with chunk_number zero is incorrect" + + +def test_hybrid_policy_batches_mode(index, vector_query): + """Test vector query with BATCHES hybrid policy.""" + # Create a filter + t = Tag("credit_score") == "high" + + # Set hybrid policy to BATCHES + vector_query.set_hybrid_policy("BATCHES") + vector_query.set_batch_size(2) + + # Set the filter + vector_query.set_filter(t) + + # Check query string + assert "HYBRID_POLICY BATCHES BATCH_SIZE 2" in str(vector_query) + + # Execute query + results = index.query(vector_query) + + # Check results - should have filtered to "high" credit scores + assert len(results) > 0 + for result in results: + assert result["credit_score"] == "high" + + +def test_hybrid_policy_adhoc_bf_mode(index, vector_query): + """Test vector query with ADHOC_BF hybrid policy.""" + # Create a filter + t = Tag("credit_score") == "high" + + # Set hybrid policy to ADHOC_BF + vector_query.set_hybrid_policy("ADHOC_BF") + + # Set the filter + vector_query.set_filter(t) + + # Check query string + assert "HYBRID_POLICY ADHOC_BF" in str(vector_query) + + # Execute query + results = index.query(vector_query) + + # Check results - should have filtered to "high" credit scores + assert len(results) > 0 + for result in results: + assert result["credit_score"] == "high" + + +def test_range_query_with_epsilon(index): + """Integration test: Execute range query with epsilon parameter against Redis.""" + # Create a range query with epsilon + epsilon_query = VectorRangeQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + return_fields=["user", "credit_score", "age", "job"], + distance_threshold=0.3, + epsilon=0.5, # Larger than default to get potentially more results + ) + + # Verify query string contains epsilon attribute + query_string = str(epsilon_query) + assert "$EPSILON: 0.5" in query_string + + # Verify epsilon property is set + assert epsilon_query.epsilon == 0.5 + + # Test setting epsilon + epsilon_query.set_epsilon(0.1) + assert epsilon_query.epsilon == 0.1 + assert "$EPSILON: 0.1" in str(epsilon_query) + + # Execute basic query without epsilon to ensure functionality + basic_query = VectorRangeQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + return_fields=["user", "credit_score", "age", "job"], + distance_threshold=0.2, + ) + + results = index.query(basic_query) + + # Check results + for result in results: + assert float(result["vector_distance"]) <= 0.2 + + +def test_range_query_with_filter_and_hybrid_policy(index): + """Integration test: Test construction of a range query with filter and hybrid policy.""" + # Create a filter for high credit score + credit_filter = Tag("credit_score") == "high" + + # Create a range query with filter and hybrid policy + query = VectorRangeQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + return_fields=["user", "credit_score", "age", "job"], + filter_expression=credit_filter, + distance_threshold=0.5, + hybrid_policy="BATCHES", + batch_size=2, + ) + + # Check query string and parameters + query_string = str(query) + assert "@credit_score:{high}" in query_string + assert "HYBRID_POLICY" not in query_string + assert query.hybrid_policy == "BATCHES" + assert query.batch_size == 2 + assert query.params["HYBRID_POLICY"] == "BATCHES" + assert query.params["BATCH_SIZE"] == 2 + + # Execute basic query with filter but without hybrid policy + basic_filter_query = VectorRangeQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + return_fields=["user", "credit_score", "age", "job"], + filter_expression=credit_filter, + distance_threshold=0.5, + ) + + results = index.query(basic_filter_query) + + # Check results + for result in results: + assert result["credit_score"] == "high" + assert float(result["vector_distance"]) <= 0.5 + + +def test_query_normalize_cosine_distance(index, normalized_vector_query): + + res = index.query(normalized_vector_query) + + for r in res: + assert 0 <= float(r["vector_distance"]) <= 1 + + +def test_query_cosine_distance_un_normalized(index, vector_query): + + res = index.query(vector_query) + + assert any(float(r["vector_distance"]) > 1 for r in res) + + +def test_query_l2_distance_un_normalized(L2_index, vector_query): + + res = L2_index.query(vector_query) + + assert any(float(r["vector_distance"]) > 1 for r in res) + + +def test_query_l2_distance_normalized(L2_index, normalized_vector_query): + + res = L2_index.query(normalized_vector_query) + + for r in res: + assert 0 <= float(r["vector_distance"]) <= 1 + + +def test_range_query_normalize_cosine_distance(index, normalized_range_query): + + res = index.query(normalized_range_query) + + for r in res: + assert 0 <= float(r["vector_distance"]) <= 1 + + +def test_range_query_normalize_bad_input(index): + with pytest.raises(ValueError): + VectorRangeQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + normalize_vector_distance=True, + return_score=True, + return_fields=["user", "credit_score", "age", "job", "location"], + distance_threshold=1.2, + ) + + +@pytest.mark.parametrize( + "scorer", ["BM25", "TFIDF", "TFIDF.DOCNORM", "DISMAX", "DOCSCORE"] +) +def test_text_query(index, scorer): + text = "a medical professional with expertise in lung cancer" + text_field = "description" + return_fields = ["user", "credit_score", "age", "job", "location", "description"] + + text_query = TextQuery( + text=text, + text_field_name=text_field, + text_scorer=scorer, + return_fields=return_fields, + ) + results = index.query(text_query) + + assert len(results) == 4 + + # make sure at least one word from the query is in the description + for result in results: + assert any(word in result[text_field] for word in text.split()) + + +# test that text queryies work with filter expressions +def test_text_query_with_filter(index): + text = "a medical professional with expertise in lung cancer" + text_field = "description" + return_fields = ["user", "credit_score", "age", "job", "location", "description"] + filter_expression = (Tag("credit_score") == ("high")) & (Num("age") > 30) + scorer = "TFIDF" + + text_query = TextQuery( + text=text, + text_field_name=text_field, + text_scorer=scorer, + filter_expression=filter_expression, + return_fields=return_fields, + ) + results = index.query(text_query) + assert len(results) == 2 + for result in results: + assert any(word in result[text_field] for word in text.split()) + assert result["credit_score"] == "high" + assert int(result["age"]) > 30 + + +# test that text queryies workt with text filter expressions on the same text field +def test_text_query_with_text_filter(index): + text = "a medical professional with expertise in lung cancer" + text_field = "description" + scorer = "TFIDF.DOCNORM" + return_fields = ["age", "job", "description"] + filter_expression = Text(text_field) == ("medical") + + text_query = TextQuery( + text=text, + text_field_name=text_field, + text_scorer=scorer, + filter_expression=filter_expression, + return_fields=return_fields, + ) + results = index.query(text_query) + assert len(results) == 2 + for result in results: + assert any(word in result[text_field] for word in text.split()) + assert "medical" in result[text_field] + + filter_expression = Text(text_field) != ("research") + + text_query = TextQuery( + text=text, + text_field_name=text_field, + text_scorer=scorer, + filter_expression=filter_expression, + return_fields=return_fields, + ) + + results = index.query(text_query) + assert len(results) == 3 + for result in results: + assert any(word in result[text_field] for word in text.split()) + assert "research" not in result[text_field] diff --git a/tests/integration/test_search_index.py b/tests/integration/test_search_index.py index 800f6a06..875449be 100644 --- a/tests/integration/test_search_index.py +++ b/tests/integration/test_search_index.py @@ -4,7 +4,7 @@ import pytest from redis import Redis -from redisvl.exceptions import RedisModuleVersionError, RedisSearchError +from redisvl.exceptions import RedisModuleVersionError, RedisSearchError, RedisVLError from redisvl.index import SearchIndex from redisvl.query import VectorQuery from redisvl.query.query import FilterQuery @@ -268,7 +268,7 @@ def preprocess(record): def bad_preprocess(record): return 1 - with pytest.raises(TypeError): + with pytest.raises(RedisVLError): index.load(data, id_field="id", preprocess=bad_preprocess) @@ -277,7 +277,7 @@ def test_no_id_field(index): bad_data = [{"wrong_key": "1", "value": "test"}] # catch missing / invalid id_field - with pytest.raises(ValueError): + with pytest.raises(RedisVLError): index.load(bad_data, id_field="key") diff --git a/tests/integration/test_semantic_router.py b/tests/integration/test_semantic_router.py index ed8f2f0e..07750b42 100644 --- a/tests/integration/test_semantic_router.py +++ b/tests/integration/test_semantic_router.py @@ -33,7 +33,7 @@ def routes(): name="farewell", references=["bye", "goodbye"], metadata={"type": "farewell"}, - distance_threshold=0.3, + distance_threshold=0.2, ), ] @@ -43,7 +43,7 @@ def semantic_router(client, routes): router = SemanticRouter( name="test-router", routes=routes, - routing_config=RoutingConfig(distance_threshold=0.3, max_k=2), + routing_config=RoutingConfig(max_k=2), redis_client=client, overwrite=False, ) @@ -71,7 +71,7 @@ def test_router_properties(semantic_router): thresholds = semantic_router.route_thresholds assert thresholds["greeting"] == 0.3 - assert thresholds["farewell"] == 0.3 + assert thresholds["farewell"] == 0.2 def test_get_route(semantic_router): diff --git a/tests/integration/test_threshold_optimizer.py b/tests/integration/test_threshold_optimizer.py new file mode 100644 index 00000000..e227af98 --- /dev/null +++ b/tests/integration/test_threshold_optimizer.py @@ -0,0 +1,303 @@ +import sys + +import pytest + +if sys.version_info.major == 3 and sys.version_info.minor < 10: + pytest.skip("Test requires Python 3.10 or higher", allow_module_level=True) + +from redisvl.extensions.llmcache import SemanticCache +from redisvl.extensions.router import Route, SemanticRouter +from redisvl.extensions.router.schema import RoutingConfig +from redisvl.redis.connection import compare_versions +from redisvl.utils.optimize import ( + CacheThresholdOptimizer, + EvalMetric, + RouterThresholdOptimizer, +) + + +@pytest.fixture +def routes(): + return [ + Route( + name="greeting", + references=["hello", "hi"], + metadata={"type": "greeting"}, + distance_threshold=0.3, + ), + Route( + name="farewell", + references=["bye", "goodbye"], + metadata={"type": "farewell"}, + distance_threshold=0.2, + ), + ] + + +@pytest.fixture +def semantic_router(client, routes, hf_vectorizer): + router = SemanticRouter( + name="test-router", + routes=routes, + vectorizer=hf_vectorizer, + routing_config=RoutingConfig(max_k=2), + redis_client=client, + overwrite=False, + ) + yield router + router.delete() + + +@pytest.fixture +def test_data_optimization(): + return [ + # Greetings + {"query": "hello", "query_match": "greeting"}, # English + {"query": "hola", "query_match": "greeting"}, # Spanish + {"query": "bonjour", "query_match": "greeting"}, # French + {"query": "ciao", "query_match": "greeting"}, # Italian + {"query": "hallo", "query_match": "greeting"}, # German + {"query": "こんにちは", "query_match": "greeting"}, # Japanese + {"query": "안녕하세요", "query_match": "greeting"}, # Korean + {"query": "你好", "query_match": "greeting"}, # Chinese + {"query": "مرحبا", "query_match": "greeting"}, # Arabic + {"query": "привет", "query_match": "greeting"}, # Russian + {"query": "γεια σας", "query_match": "greeting"}, # Greek + {"query": "namaste", "query_match": "greeting"}, # Hindi + {"query": "olá", "query_match": "greeting"}, # Portuguese + {"query": "salut", "query_match": "greeting"}, # French informal + {"query": "cześć", "query_match": "greeting"}, # Polish + # Farewells + {"query": "goodbye", "query_match": "farewell"}, # English + {"query": "adiós", "query_match": "farewell"}, # Spanish + {"query": "au revoir", "query_match": "farewell"}, # French + {"query": "arrivederci", "query_match": "farewell"}, # Italian + {"query": "auf wiedersehen", "query_match": "farewell"}, # German + {"query": "さようなら", "query_match": "farewell"}, # Japanese + {"query": "안녕히 가세요", "query_match": "farewell"}, # Korean + {"query": "再见", "query_match": "farewell"}, # Chinese + {"query": "مع السلامة", "query_match": "farewell"}, # Arabic + {"query": "до свидания", "query_match": "farewell"}, # Russian + {"query": "αντίο", "query_match": "farewell"}, # Greek + {"query": "अलविदा", "query_match": "farewell"}, # Hindi + {"query": "adeus", "query_match": "farewell"}, # Portuguese + {"query": "tchau", "query_match": "farewell"}, # Portuguese informal + {"query": "do widzenia", "query_match": "farewell"}, # Polish + ] + + +def test_routes_different_distance_thresholds_optimizer_default( + semantic_router, routes, redis_url, test_data_optimization, hf_vectorizer +): + redis_version = semantic_router._index.client.info()["redis_version"] + if not compare_versions(redis_version, "7.0.0"): + pytest.skip("Not using a late enough version of Redis") + + zero_threshold = 0.0 + + # Test that it updates the thresholds + routes[0].distance_threshold = zero_threshold + routes[1].distance_threshold = zero_threshold + + router = SemanticRouter( + name="test_routes_different_distance_optimizer", + routes=routes, + vectorizer=hf_vectorizer, + redis_url=redis_url, + overwrite=True, + ) + + # szia is hello in hungarian and not in our test data + matches = router.route_many("Szia", max_k=2) + assert len(matches) == 0 + + # now run optimizer + router_optimizer = RouterThresholdOptimizer(router, test_data_optimization) + router_optimizer.optimize(max_iterations=10, search_step=0.5) + + # test that it updated thresholds beyond the null case + for route in routes: + assert route.distance_threshold > zero_threshold + + +def test_routes_different_distance_thresholds_optimizer_precision( + semantic_router, routes, redis_url, test_data_optimization, hf_vectorizer +): + + redis_version = semantic_router._index.client.info()["redis_version"] + if not compare_versions(redis_version, "7.0.0"): + pytest.skip("Not using a late enough version of Redis") + + zero_threshold = 0.0 + + # Test that it updates the thresholds + routes[0].distance_threshold = zero_threshold + routes[1].distance_threshold = zero_threshold + + router = SemanticRouter( + name="test_routes_different_distance_optimizer", + routes=routes, + vectorizer=hf_vectorizer, + redis_url=redis_url, + overwrite=True, + ) + + # szia is hello in hungarian and not in our test data + matches = router.route_many("Szia", max_k=2) + assert len(matches) == 0 + + # now run optimizer + router_optimizer = RouterThresholdOptimizer( + router, test_data_optimization, eval_metric="precision" + ) + router_optimizer.optimize(max_iterations=10, search_step=0.5) + + # test that it updated thresholds beyond the null case + for route in routes: + assert route.distance_threshold > zero_threshold + + +def test_routes_different_distance_thresholds_optimizer_recall( + semantic_router, routes, redis_url, test_data_optimization, hf_vectorizer +): + redis_version = semantic_router._index.client.info()["redis_version"] + if not compare_versions(redis_version, "7.0.0"): + pytest.skip("Not using a late enough version of Redis") + + zero_threshold = 0.0 + + # Test that it updates the thresholds + routes[0].distance_threshold = zero_threshold + routes[1].distance_threshold = zero_threshold + + router = SemanticRouter( + name="test_routes_different_distance_optimizer", + routes=routes, + vectorizer=hf_vectorizer, + redis_url=redis_url, + overwrite=True, + ) + + # szia is hello in hungarian and not in our test data + matches = router.route_many("Szia", max_k=2) + assert len(matches) == 0 + + # now run optimizer + router_optimizer = RouterThresholdOptimizer( + router, test_data_optimization, eval_metric="recall" + ) + router_optimizer.optimize(max_iterations=10, search_step=0.5) + + # test that it updated thresholds beyond the null case + for route in routes: + assert route.distance_threshold > zero_threshold + + +def test_optimize_threshold_cache_default(redis_url): + null_threshold = 0.0 + cache = SemanticCache( + name="test_optimize_threshold_cache", + redis_url=redis_url, + distance_threshold=null_threshold, + ) + + redis_version = cache._index.client.info()["redis_version"] + if not compare_versions(redis_version, "7.0.0"): + pytest.skip("Not using a late enough version of Redis") + + paris_key = cache.store(prompt="what is the capital of france?", response="paris") + rabat_key = cache.store(prompt="what is the capital of morocco?", response="rabat") + + test_dict = [ + {"query": "what actually is the capital of france?", "query_match": paris_key}, + {"query": "what actually is the capital of morocco?", "query_match": rabat_key}, + {"query": "What is the state bird of virginia?", "query_match": ""}, + ] + + cache_optimizer = CacheThresholdOptimizer(cache, test_dict) + + cache_optimizer.optimize() + + assert cache.distance_threshold > null_threshold + + +def test_optimize_threshold_cache_precision(client, redis_url): + redis_version = client.info()["redis_version"] + if not compare_versions(redis_version, "7.0.0"): + pytest.skip("Not using a late enough version of Redis") + + null_threshold = 0.0 + cache = SemanticCache( + name="test_optimize_threshold_cache", + redis_url=redis_url, + distance_threshold=null_threshold, + ) + + paris_key = cache.store(prompt="what is the capital of france?", response="paris") + rabat_key = cache.store(prompt="what is the capital of morocco?", response="rabat") + + test_dict = [ + {"query": "what actually is the capital of france?", "query_match": paris_key}, + {"query": "what actually is the capital of morocco?", "query_match": rabat_key}, + {"query": "What is the state bird of virginia?", "query_match": ""}, + ] + + cache_optimizer = CacheThresholdOptimizer(cache, test_dict, eval_metric="precision") + + cache_optimizer.optimize() + + assert cache.distance_threshold > null_threshold + + +def test_optimize_threshold_cache_recall(client, redis_url): + redis_version = client.info()["redis_version"] + if not compare_versions(redis_version, "7.0.0"): + pytest.skip("Not using a late enough version of Redis") + + null_threshold = 0.0 + cache = SemanticCache( + name="test_optimize_threshold_cache", + redis_url=redis_url, + distance_threshold=null_threshold, + ) + + paris_key = cache.store(prompt="what is the capital of france?", response="paris") + rabat_key = cache.store(prompt="what is the capital of morocco?", response="rabat") + + test_dict = [ + {"query": "what actually is the capital of france?", "query_match": paris_key}, + {"query": "what actually is the capital of morocco?", "query_match": rabat_key}, + {"query": "What is the state bird of virginia?", "query_match": ""}, + ] + + cache_optimizer = CacheThresholdOptimizer(cache, test_dict, eval_metric="recall") + + cache_optimizer.optimize() + + assert cache.distance_threshold > null_threshold + + +def test_eval_metric_from_string(): + """Test that EvalMetric.from_string works for valid metrics.""" + assert EvalMetric("f1") == EvalMetric.F1 + assert EvalMetric("precision") == EvalMetric.PRECISION + assert EvalMetric("recall") == EvalMetric.RECALL + + +def test_eval_metric_invalid(): + """Test that EvalMetric.from_string raises ValueError for invalid metrics.""" + with pytest.raises(ValueError): + EvalMetric("invalid_metric") + + +def test_optimizer_with_invalid_metric(redis_url): + """Test that optimizers raise ValueError when initialized with invalid metric.""" + cache = SemanticCache( + name="test_invalid_metric", + redis_url=redis_url, + ) + + test_dict = [{"query": "test", "query_match": ""}] + + with pytest.raises(ValueError): + CacheThresholdOptimizer(cache, test_dict, eval_metric="invalid_metric") diff --git a/tests/unit/logger_interference_checker.py b/tests/unit/logger_interference_checker.py new file mode 100644 index 00000000..7d1c9ca9 --- /dev/null +++ b/tests/unit/logger_interference_checker.py @@ -0,0 +1,25 @@ +import logging +import sys + +# Set up custom logging +handler = logging.StreamHandler(sys.stdout) +handler.setFormatter( + logging.Formatter( + "%(asctime)s %(levelname)s [%(name)s] [%(filename)s:%(lineno)s] %(message)s" + ) +) + +# Configure root logger +root_logger = logging.getLogger() +root_logger.handlers = [handler] +root_logger.setLevel(logging.INFO) + +# Log before import +app_logger = logging.getLogger("app") +app_logger.info("PRE_IMPORT_FORMAT") + +# Import RedisVL +from redisvl.query.filter import Text # noqa: E402, F401 + +# Log after import +app_logger.info("POST_IMPORT_FORMAT") diff --git a/tests/unit/test_aggregation_types.py b/tests/unit/test_aggregation_types.py new file mode 100644 index 00000000..aaf34d5d --- /dev/null +++ b/tests/unit/test_aggregation_types.py @@ -0,0 +1,115 @@ +import pytest +from redis.commands.search.aggregation import AggregateRequest +from redis.commands.search.query import Query +from redis.commands.search.result import Result + +from redisvl.index.index import process_results +from redisvl.query.aggregate import HybridQuery +from redisvl.query.filter import Tag + +# Sample data for testing +sample_vector = [0.1, 0.2, 0.3, 0.4] +sample_text = "the toon squad play basketball against a gang of aliens" + + +# Test Cases +def test_aggregate_hybrid_query(): + text_field_name = "description" + vector_field_name = "embedding" + + hybrid_query = HybridQuery( + text=sample_text, + text_field_name=text_field_name, + vector=sample_vector, + vector_field_name=vector_field_name, + ) + + assert isinstance(hybrid_query, AggregateRequest) + + # Check defaut properties + assert hybrid_query._text == sample_text + assert hybrid_query._text_field == text_field_name + assert hybrid_query._vector == sample_vector + assert hybrid_query._vector_field == vector_field_name + assert hybrid_query._scorer == "BM25STD" + assert hybrid_query._filter_expression == None + assert hybrid_query._alpha == 0.7 + assert hybrid_query._num_results == 10 + assert hybrid_query._loadfields == [] + assert hybrid_query._dialect == 2 + + # Check specifying properties + scorer = "TFIDF" + filter_expression = Tag("genre") == "comedy" + alpha = 0.5 + num_results = 8 + return_fields = ["title", "genre", "rating"] + stopwords = [] + dialect = 2 + + hybrid_query = HybridQuery( + text=sample_text, + text_field_name=text_field_name, + vector=sample_vector, + vector_field_name=vector_field_name, + text_scorer=scorer, + filter_expression=filter_expression, + alpha=alpha, + num_results=num_results, + return_fields=return_fields, + stopwords=stopwords, + dialect=dialect, + ) + + assert hybrid_query._text == sample_text + assert hybrid_query._text_field == text_field_name + assert hybrid_query._vector == sample_vector + assert hybrid_query._vector_field == vector_field_name + assert hybrid_query._scorer == scorer + assert hybrid_query._filter_expression == filter_expression + assert hybrid_query._alpha == 0.5 + assert hybrid_query._num_results == 8 + assert hybrid_query._loadfields == return_fields + assert hybrid_query._dialect == 2 + assert hybrid_query.stopwords == set() + + # Test stopwords are configurable + hybrid_query = HybridQuery( + sample_text, text_field_name, sample_vector, vector_field_name, stopwords=None + ) + assert hybrid_query.stopwords == set([]) + + hybrid_query = HybridQuery( + sample_text, + text_field_name, + sample_vector, + vector_field_name, + stopwords=["the", "a", "of"], + ) + assert hybrid_query.stopwords == set(["the", "a", "of"]) + hybrid_query = HybridQuery( + sample_text, + text_field_name, + sample_vector, + vector_field_name, + stopwords="german", + ) + assert hybrid_query.stopwords != set([]) + + with pytest.raises(ValueError): + hybrid_query = HybridQuery( + sample_text, + text_field_name, + sample_vector, + vector_field_name, + stopwords="gibberish", + ) + + with pytest.raises(TypeError): + hybrid_query = HybridQuery( + sample_text, + text_field_name, + sample_vector, + vector_field_name, + stopwords=[1, 2, 3], + ) diff --git a/tests/unit/test_fields.py b/tests/unit/test_fields.py index f420afff..0c0d504e 100644 --- a/tests/unit/test_fields.py +++ b/tests/unit/test_fields.py @@ -1,3 +1,5 @@ +from typing import Any, Optional, Tuple + import pytest from redis.commands.search.field import GeoField as RedisGeoField from redis.commands.search.field import NumericField as RedisNumericField diff --git a/tests/unit/test_filter.py b/tests/unit/test_filter.py index 067402ea..dae74240 100644 --- a/tests/unit/test_filter.py +++ b/tests/unit/test_filter.py @@ -1,6 +1,8 @@ +from datetime import date, datetime, time, timedelta, timezone + import pytest -from redisvl.query.filter import Geo, GeoRadius, Num, Tag, Text +from redisvl.query.filter import Geo, GeoRadius, Num, Tag, Text, Timestamp # Test cases for various scenarios of tag usage, combinations, and their string representations. @@ -110,6 +112,18 @@ def test_numeric_filter(): nf = Num("numeric_field") != None assert str(nf) == "*" + nf = Num("numeric_field").between(2, 5) + assert str(nf) == "@numeric_field:[2 5]" + + nf = Num("numeric_field").between(2, 5, inclusive="neither") + assert str(nf) == "@numeric_field:[2 5]" + + nf = Num("numeric_field").between(2, 5, inclusive="left") + assert str(nf) == "@numeric_field:[2 (5]" + + nf = Num("numeric_field").between(2, 5, inclusive="right") + assert str(nf) == "@numeric_field:[(2 5]" + def test_text_filter(): txt_f = Text("text_field") == "text" @@ -292,3 +306,179 @@ def test_num_filter_zero(): assert ( str(num_filter) == "@chunk_number:[0 0]" ), "Num filter should handle zero correctly" + + +def test_timestamp_datetime(): + """Test Timestamp filter with datetime objects.""" + # Test with timezone-aware datetime + dt = datetime(2023, 3, 17, 14, 30, 0, tzinfo=timezone.utc) + ts = Timestamp("created_at") == dt + # Expected timestamp would be the Unix timestamp for the datetime + expected_ts = dt.timestamp() + assert str(ts) == f"@created_at:[{expected_ts} {expected_ts}]" + + # Test with timezone-naive datetime (should convert to UTC) + dt = datetime(2023, 3, 17, 14, 30, 0) + ts = Timestamp("created_at") == dt + expected_ts = dt.replace(tzinfo=timezone.utc).timestamp() + assert str(ts) == f"@created_at:[{expected_ts} {expected_ts}]" + + +def test_timestamp_date(): + """Test Timestamp filter with date objects (should match full day).""" + d = date(2023, 3, 17) + ts = Timestamp("created_at") == d + + expected_ts_start = ( + datetime.combine(d, time.min).astimezone(timezone.utc).timestamp() + ) + expected_ts_end = datetime.combine(d, time.max).astimezone(timezone.utc).timestamp() + + assert str(ts) == f"@created_at:[{expected_ts_start} {expected_ts_end}]" + + +def test_timestamp_iso_string(): + """Test Timestamp filter with ISO format strings.""" + # Date-only ISO string + ts = Timestamp("created_at") == "2023-03-17" + d = date(2023, 3, 17) + expected_ts_start = ( + datetime.combine(d, time.min).astimezone(timezone.utc).timestamp() + ) + expected_ts_end = datetime.combine(d, time.max).astimezone(timezone.utc).timestamp() + assert str(ts) == f"@created_at:[{expected_ts_start} {expected_ts_end}]" + + # Full ISO datetime string + dt_str = "2023-03-17T14:30:00+00:00" + ts = Timestamp("created_at") == dt_str + dt = datetime.fromisoformat(dt_str) + expected_ts = dt.timestamp() + assert str(ts) == f"@created_at:[{expected_ts} {expected_ts}]" + + +def test_timestamp_unix(): + """Test Timestamp filter with Unix timestamps.""" + # Integer timestamp + ts = Timestamp("created_at") == 1679062200 # 2023-03-17T14:30:00+00:00 + assert str(ts) == "@created_at:[1679062200.0 1679062200.0]" + + # Float timestamp + ts = Timestamp("created_at") == 1679062200.5 + assert str(ts) == "@created_at:[1679062200.5 1679062200.5]" + + +def test_timestamp_operators(): + """Test all comparison operators for Timestamp filter.""" + dt = datetime(2023, 3, 17, 14, 30, 0, tzinfo=timezone.utc) + ts_value = dt.timestamp() + + # Equal + ts = Timestamp("created_at") == dt + assert str(ts) == f"@created_at:[{ts_value} {ts_value}]" + + # Not equal + ts = Timestamp("created_at") != dt + assert str(ts) == f"(-@created_at:[{ts_value} {ts_value}])" + + # Greater than + ts = Timestamp("created_at") > dt + assert str(ts) == f"@created_at:[({ts_value} +inf]" + + # Less than + ts = Timestamp("created_at") < dt + assert str(ts) == f"@created_at:[-inf ({ts_value}]" + + # Greater than or equal + ts = Timestamp("created_at") >= dt + assert str(ts) == f"@created_at:[{ts_value} +inf]" + + # Less than or equal + ts = Timestamp("created_at") <= dt + assert str(ts) == f"@created_at:[-inf {ts_value}]" + + td = timedelta(days=5) + dt2 = dt + td + ts_value2 = dt2.timestamp() + + ts = Timestamp("created_at").between(dt, dt2) + assert str(ts) == f"@created_at:[{ts_value} {ts_value2}]" + + ts = Timestamp("created_at").between(dt, dt2, inclusive="neither") + assert str(ts) == f"@created_at:[({ts_value} ({ts_value2}]" + + ts = Timestamp("created_at").between(dt, dt2, inclusive="left") + assert str(ts) == f"@created_at:[{ts_value} ({ts_value2}]" + + ts = Timestamp("created_at").between(dt, dt2, inclusive="right") + assert str(ts) == f"@created_at:[({ts_value} {ts_value2}]" + + +def test_timestamp_between(): + """Test the between method for date ranges.""" + start = datetime(2023, 3, 1, 0, 0, 0, tzinfo=timezone.utc) + end = datetime(2023, 3, 31, 23, 59, 59, tzinfo=timezone.utc) + + ts = Timestamp("created_at").between(start, end) + + start_ts = start.timestamp() + end_ts = end.timestamp() + + assert str(ts) == f"@created_at:[{start_ts} {end_ts}]" + + # Test with dates (should expand to full days) + start_date = date(2023, 3, 1) + end_date = date(2023, 3, 31) + + ts = Timestamp("created_at").between(start_date, end_date) + + # Start should be beginning of day + expected_start = datetime.combine(start_date, datetime.min.time()) + expected_start = expected_start.replace(tzinfo=timezone.utc) + + # End should be end of day + expected_end = datetime.combine(end_date, datetime.max.time()) + expected_end = expected_end.replace(tzinfo=timezone.utc) + + expected_start_ts = expected_start.timestamp() + expected_end_ts = expected_end.timestamp() + + assert str(ts) == f"@created_at:[{expected_start_ts} {expected_end_ts}]" + + +def test_timestamp_none(): + """Test handling of None values.""" + ts = Timestamp("created_at") == None + assert str(ts) == "*" + + ts = Timestamp("created_at") != None + assert str(ts) == "*" + + ts = Timestamp("created_at") > None + assert str(ts) == "*" + + +def test_timestamp_invalid_input(): + """Test error handling for invalid inputs.""" + # Invalid ISO format + with pytest.raises(ValueError): + Timestamp("created_at") == "not-a-date" + + # Unsupported type + with pytest.raises(TypeError): + Timestamp("created_at") == object() + + +def test_timestamp_filter_combination(): + """Test combining timestamp filters with other filters.""" + from redisvl.query.filter import Num, Tag + + ts = Timestamp("created_at") > datetime(2023, 3, 1) + num = Num("age") > 30 + tag = Tag("status") == "active" + + combined = ts & num & tag + + # The exact string depends on the timestamp value, but we can check structure + assert str(combined).startswith("((@created_at:") + assert "@age:[(30 +inf]" in str(combined) + assert "@status:{active}" in str(combined) diff --git a/tests/unit/test_query_types.py b/tests/unit/test_query_types.py index 1e9fdb08..546e7675 100644 --- a/tests/unit/test_query_types.py +++ b/tests/unit/test_query_types.py @@ -3,8 +3,9 @@ from redis.commands.search.result import Result from redisvl.index.index import process_results -from redisvl.query import CountQuery, FilterQuery, RangeQuery, VectorQuery +from redisvl.query import CountQuery, FilterQuery, RangeQuery, TextQuery, VectorQuery from redisvl.query.filter import Tag +from redisvl.query.query import VectorRangeQuery # Sample data for testing sample_vector = [0.1, 0.2, 0.3, 0.4] @@ -48,8 +49,8 @@ def test_filter_query(): assert isinstance(filter_query.params, dict) assert filter_query.params == {} assert filter_query._dialect == 2 - assert filter_query._sortby == None - assert filter_query._in_order == False + assert filter_query._sortby is None + assert filter_query._in_order is False # Test set_filter functionality new_filter_expression = Tag("category") == "Sportswear" @@ -92,7 +93,7 @@ def test_vector_query(): assert vector_query.params != {} assert vector_query._dialect == 3 assert vector_query._sortby.args[0] == VectorQuery.DISTANCE_ID - assert vector_query._in_order == False + assert vector_query._in_order is False # Test set_filter functionality new_filter_expression = Tag("category") == "Sportswear" @@ -187,6 +188,94 @@ def test_range_query(): assert range_query._in_order +def test_text_query(): + text_string = "the toon squad play basketball against a gang of aliens" + text_field_name = "description" + return_fields = ["title", "genre", "rating"] + text_query = TextQuery( + text=text_string, + text_field_name=text_field_name, + return_fields=return_fields, + text_scorer="BM25", + return_score=False, + ) + + # Check properties + assert text_query._return_fields == return_fields + assert text_query._num_results == 10 + + assert isinstance(text_query, Query) + assert isinstance(text_query.query, Query) + assert isinstance(text_query.params, dict) + assert text_query.params == {} + assert text_query._dialect == 2 + assert text_query._in_order == False + + # Test paging functionality + text_query.paging(5, 7) + assert text_query._offset == 5 + assert text_query._num == 7 + assert text_query._num_results == 10 + + # Test sort_by functionality + filter_expression = Tag("genre") == "comedy" + scorer = "TFIDF" + text_query = TextQuery( + text_string, + text_field_name, + scorer, + filter_expression, + return_fields, + num_results=10, + sort_by="rating", + ) + assert text_query._sortby is not None + + # Test in_order functionality + text_query = TextQuery( + text_string, + text_field_name, + scorer, + filter_expression, + return_fields, + num_results=10, + in_order=True, + ) + assert text_query._in_order + + # Test stopwords are configurable + text_query = TextQuery(text_string, text_field_name, stopwords=None) + assert text_query.stopwords == set([]) + + text_query = TextQuery(text_string, text_field_name, stopwords=["the", "a", "of"]) + assert text_query.stopwords == set(["the", "a", "of"]) + + text_query = TextQuery(text_string, text_field_name, stopwords="german") + assert text_query.stopwords != set([]) + + with pytest.raises(ValueError): + text_query = TextQuery(text_string, text_field_name, stopwords="gibberish") + + with pytest.raises(TypeError): + text_query = TextQuery(text_string, text_field_name, stopwords=[1, 2, 3]) + + text_query = TextQuery(text_string, text_field_name, stopwords=["the", "a", "of"]) + assert text_query.stopwords == set(["the", "a", "of"]) + + text_query = TextQuery(text_string, text_field_name, stopwords="german") + assert text_query.stopwords != set([]) + + # test that filter expression is set correctly + text_query.set_filter(filter_expression) + assert text_query.filter == filter_expression + + with pytest.raises(ValueError): + text_query = TextQuery(text_string, text_field_name, stopwords="gibberish") + + with pytest.raises(TypeError): + text_query = TextQuery(text_string, text_field_name, stopwords=[1, 2, 3]) + + @pytest.mark.parametrize( "query", [ @@ -277,3 +366,271 @@ def test_string_filter_expressions(query): query.set_filter("~(@desciption:(hello | world))") assert query._filter_expression == "~(@desciption:(hello | world))" assert query.query_string().__contains__("~(@desciption:(hello | world))") + + +def test_vector_query_hybrid_policy(): + """Test that VectorQuery correctly handles hybrid policy parameters.""" + # Create a vector query with hybrid policy + vector_query = VectorQuery( + [0.1, 0.2, 0.3, 0.4], "vector_field", hybrid_policy="BATCHES" + ) + + # Check properties + assert vector_query.hybrid_policy == "BATCHES" + assert vector_query.batch_size is None + + # Check query string + query_string = str(vector_query) + assert "HYBRID_POLICY BATCHES" in query_string + + # Test with batch size + vector_query = VectorQuery( + [0.1, 0.2, 0.3, 0.4], "vector_field", hybrid_policy="BATCHES", batch_size=50 + ) + + # Check properties + assert vector_query.hybrid_policy == "BATCHES" + assert vector_query.batch_size == 50 + + # Check query string + query_string = str(vector_query) + assert "HYBRID_POLICY BATCHES BATCH_SIZE 50" in query_string + + # Test with ADHOC_BF policy + vector_query = VectorQuery( + [0.1, 0.2, 0.3, 0.4], "vector_field", hybrid_policy="ADHOC_BF" + ) + + # Check properties + assert vector_query.hybrid_policy == "ADHOC_BF" + + # Check query string + query_string = str(vector_query) + assert "HYBRID_POLICY ADHOC_BF" in query_string + + +def test_vector_query_set_hybrid_policy(): + """Test that VectorQuery setter methods work properly.""" + # Create a vector query + vector_query = VectorQuery([0.1, 0.2, 0.3, 0.4], "vector_field") + + # Initially no hybrid policy + assert vector_query.hybrid_policy is None + assert "HYBRID_POLICY" not in str(vector_query) + + # Set hybrid policy + vector_query.set_hybrid_policy("BATCHES") + + # Check properties + assert vector_query.hybrid_policy == "BATCHES" + + # Check query string + query_string = str(vector_query) + assert "HYBRID_POLICY BATCHES" in query_string + + # Set batch size + vector_query.set_batch_size(100) + + # Check properties + assert vector_query.batch_size == 100 + + # Check query string + query_string = str(vector_query) + assert "HYBRID_POLICY BATCHES BATCH_SIZE 100" in query_string + + +def test_vector_query_invalid_hybrid_policy(): + """Test error handling for invalid hybrid policy values.""" + # Test with invalid hybrid policy + with pytest.raises(ValueError, match=r"hybrid_policy must be one of.*"): + VectorQuery([0.1, 0.2, 0.3, 0.4], "vector_field", hybrid_policy="INVALID") + + # Create a valid vector query + vector_query = VectorQuery([0.1, 0.2, 0.3, 0.4], "vector_field") + + # Test with invalid hybrid policy + with pytest.raises(ValueError, match=r"hybrid_policy must be one of.*"): + vector_query.set_hybrid_policy("INVALID") + + # Test with invalid batch size types + with pytest.raises(TypeError, match="batch_size must be an integer"): + vector_query.set_batch_size("50") + + # Test with invalid batch size values + with pytest.raises(ValueError, match="batch_size must be positive"): + vector_query.set_batch_size(0) + + with pytest.raises(ValueError, match="batch_size must be positive"): + vector_query.set_batch_size(-10) + + +def test_vector_range_query_epsilon(): + """Test that VectorRangeQuery correctly handles epsilon parameter.""" + # Create a range query with epsilon + range_query = VectorRangeQuery( + [0.1, 0.2, 0.3, 0.4], "vector_field", epsilon=0.05, distance_threshold=0.3 + ) + + # Check properties + assert range_query.epsilon == 0.05 + assert range_query.distance_threshold == 0.3 + + # Check query string + query_string = str(range_query) + assert "$EPSILON: 0.05" in query_string + + # Test setting epsilon + range_query.set_epsilon(0.1) + assert range_query.epsilon == 0.1 + assert "$EPSILON: 0.1" in str(range_query) + + +def test_vector_range_query_invalid_epsilon(): + """Test error handling for invalid epsilon values.""" + # Test with invalid epsilon type + with pytest.raises(TypeError, match="epsilon must be of type float or int"): + VectorRangeQuery([0.1, 0.2, 0.3, 0.4], "vector_field", epsilon="0.05") + + # Test with negative epsilon + with pytest.raises(ValueError, match="epsilon must be non-negative"): + VectorRangeQuery([0.1, 0.2, 0.3, 0.4], "vector_field", epsilon=-0.05) + + # Create a valid range query + range_query = VectorRangeQuery([0.1, 0.2, 0.3, 0.4], "vector_field") + + # Test with invalid epsilon + with pytest.raises(TypeError, match="epsilon must be of type float or int"): + range_query.set_epsilon("0.05") + + with pytest.raises(ValueError, match="epsilon must be non-negative"): + range_query.set_epsilon(-0.05) + + +def test_vector_range_query_construction(): + """Unit test: Test the construction of VectorRangeQuery with various parameters.""" + # Basic range query + basic_query = VectorRangeQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + return_fields=["user", "credit_score"], + distance_threshold=0.2, + ) + + query_string = str(basic_query) + assert "VECTOR_RANGE $distance_threshold $vector" in query_string + assert "$YIELD_DISTANCE_AS: vector_distance" in query_string + assert "HYBRID_POLICY" not in query_string + + # Range query with epsilon + epsilon_query = VectorRangeQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + return_fields=["user", "credit_score"], + distance_threshold=0.2, + epsilon=0.05, + ) + + query_string = str(epsilon_query) + assert "VECTOR_RANGE $distance_threshold $vector" in query_string + assert "$YIELD_DISTANCE_AS: vector_distance" in query_string + assert "$EPSILON: 0.05" in query_string + assert epsilon_query.epsilon == 0.05 + assert "EPSILON" not in epsilon_query.params + + # Range query with hybrid policy + hybrid_query = VectorRangeQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + return_fields=["user", "credit_score"], + distance_threshold=0.2, + hybrid_policy="BATCHES", + ) + + query_string = str(hybrid_query) + # Hybrid policy should not be in the query string + assert "HYBRID_POLICY" not in query_string + assert hybrid_query.hybrid_policy == "BATCHES" + assert hybrid_query.params["HYBRID_POLICY"] == "BATCHES" + + # Range query with hybrid policy and batch size + batch_query = VectorRangeQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + return_fields=["user", "credit_score"], + distance_threshold=0.2, + hybrid_policy="BATCHES", + batch_size=50, + ) + + query_string = str(batch_query) + # Hybrid policy and batch size should not be in the query string + assert "HYBRID_POLICY" not in query_string + assert "BATCH_SIZE" not in query_string + assert batch_query.hybrid_policy == "BATCHES" + assert batch_query.batch_size == 50 + assert batch_query.params["HYBRID_POLICY"] == "BATCHES" + assert batch_query.params["BATCH_SIZE"] == 50 + + +def test_vector_range_query_setter_methods(): + """Unit test: Test setter methods for VectorRangeQuery parameters.""" + # Create a basic query + query = VectorRangeQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + distance_threshold=0.2, + ) + + # Verify initial state + assert query.epsilon is None + assert query.hybrid_policy is None + assert query.batch_size is None + assert "$EPSILON" not in str(query) + assert "HYBRID_POLICY" not in query.params + assert "BATCH_SIZE" not in query.params + + # Set epsilon + query.set_epsilon(0.1) + assert query.epsilon == 0.1 + assert "$EPSILON: 0.1" in str(query) + + # Set hybrid policy + query.set_hybrid_policy("BATCHES") + assert query.hybrid_policy == "BATCHES" + assert query.params["HYBRID_POLICY"] == "BATCHES" + + # Set batch size + query.set_batch_size(25) + assert query.batch_size == 25 + assert query.params["BATCH_SIZE"] == 25 + + +def test_vector_range_query_error_handling(): + """Unit test: Test error handling for invalid VectorRangeQuery parameters.""" + # Create a basic query + query = VectorRangeQuery( + vector=[0.1, 0.1, 0.5], + vector_field_name="user_embedding", + distance_threshold=0.2, + ) + + # Test invalid epsilon + with pytest.raises(TypeError, match="epsilon must be of type float or int"): + query.set_epsilon("0.1") + + with pytest.raises(ValueError, match="epsilon must be non-negative"): + query.set_epsilon(-0.1) + + # Test invalid hybrid policy + with pytest.raises(ValueError, match="hybrid_policy must be one of"): + query.set_hybrid_policy("INVALID") + + # Test invalid batch size + with pytest.raises(TypeError, match="batch_size must be an integer"): + query.set_batch_size(10.5) + + with pytest.raises(ValueError, match="batch_size must be positive"): + query.set_batch_size(0) + + with pytest.raises(ValueError, match="batch_size must be positive"): + query.set_batch_size(-10) diff --git a/tests/unit/test_storage.py b/tests/unit/test_storage.py index 21637dd5..11f51e73 100644 --- a/tests/unit/test_storage.py +++ b/tests/unit/test_storage.py @@ -1,13 +1,99 @@ import pytest +from pydantic import ValidationError +from redisvl.exceptions import SchemaValidationError from redisvl.index.storage import BaseStorage, HashStorage, JsonStorage +from redisvl.schema import IndexSchema + + +@pytest.fixture +def sample_hash_schema(): + """Create a sample schema with HASH storage for testing.""" + schema_dict = { + "index": { + "name": "test-hash-index", + "prefix": "test", + "key_separator": ":", + "storage_type": "hash", + }, + "fields": [ + {"name": "test_id", "type": "tag"}, + {"name": "title", "type": "text"}, + {"name": "user", "type": "tag"}, + {"name": "rating", "type": "numeric"}, + {"name": "location", "type": "geo"}, + { + "name": "embedding", + "type": "vector", + "attrs": { + "algorithm": "flat", + "dims": 4, + "datatype": "float32", + "distance_metric": "cosine", + }, + }, + { + "name": "int_vector", + "type": "vector", + "attrs": { + "algorithm": "flat", + "dims": 3, + "datatype": "int8", + "distance_metric": "l2", + }, + }, + ], + } + return IndexSchema.from_dict(schema_dict) + + +@pytest.fixture +def sample_json_schema(): + """Create a sample schema with JSON storage for testing.""" + schema_dict = { + "index": { + "name": "test-json-index", + "prefix": "test", + "key_separator": ":", + "storage_type": "json", + }, + "fields": [ + {"name": "test_id", "type": "tag"}, + {"name": "user", "type": "tag"}, + {"name": "title", "type": "text"}, + {"name": "rating", "type": "numeric"}, + {"name": "location", "type": "geo"}, + { + "name": "embedding", + "type": "vector", + "attrs": { + "algorithm": "flat", + "dims": 4, + "datatype": "float32", + "distance_metric": "cosine", + }, + }, + { + "name": "int_vector", + "type": "vector", + "attrs": { + "algorithm": "flat", + "dims": 3, + "datatype": "int8", + "distance_metric": "l2", + }, + }, + ], + } + return IndexSchema.from_dict(schema_dict) @pytest.fixture(params=[JsonStorage, HashStorage]) -def storage_instance(request): +def storage_instance(request, sample_hash_schema, sample_json_schema): StorageClass = request.param - instance = StorageClass(prefix="test", key_separator=":") - return instance + if isinstance(StorageClass, JsonStorage): + return StorageClass(index_schema=sample_json_schema) + return StorageClass(index_schema=sample_hash_schema) def test_key_formatting(storage_instance): @@ -25,9 +111,7 @@ def test_key_formatting(storage_instance): def test_create_key(storage_instance): id_field = "id" obj = {id_field: "1234"} - expected_key = ( - f"{storage_instance.prefix}{storage_instance.key_separator}{obj[id_field]}" - ) + expected_key = f"{storage_instance.index_schema.index.prefix}{storage_instance.index_schema.index.key_separator}{obj[id_field]}" generated_key = storage_instance._create_key(obj, id_field) assert ( generated_key == expected_key @@ -35,46 +119,49 @@ def test_create_key(storage_instance): def test_validate_success(storage_instance): - data = {"foo": "bar"} try: - storage_instance._validate(data) + storage_instance._validate( + {"test_id": "1234", "rating": 5, "user": "john", "title": "engineer"} + ) except Exception as e: pytest.fail(f"_validate should not raise an exception here, but raised {e}") def test_validate_failure(storage_instance): - data = "Some invalid data type" - with pytest.raises(TypeError): - storage_instance._validate(data) - data = 12345 - with pytest.raises(TypeError): + data = {"title": 5} + with pytest.raises(ValidationError): storage_instance._validate(data) + data = {"user": [1]} + with pytest.raises(ValidationError): + storage_instance._validate(data) -def test_preprocess(storage_instance): - data = {"key": "value"} - preprocessed_data = storage_instance._preprocess(preprocess=None, obj=data) - assert preprocessed_data == data - def fn(d): - d["foo"] = "bar" - return d +def test_validate_preprocess_and_validate_failure(storage_instance): + data = {"title": 5} + data == storage_instance._preprocess_and_validate_objects( + objects=[data], validate=False + ) + with pytest.raises(SchemaValidationError): + storage_instance._preprocess_and_validate_objects(objects=[data], validate=True) - preprocessed_data = storage_instance._preprocess(fn, data) - assert "foo" in preprocessed_data - assert preprocessed_data["foo"] == "bar" + data = {"user": [1]} + data == storage_instance._preprocess_and_validate_objects( + objects=[data], validate=False + ) + with pytest.raises(SchemaValidationError): + storage_instance._preprocess_and_validate_objects(objects=[data], validate=True) -@pytest.mark.asyncio -async def test_preprocess(storage_instance): +def test_preprocess(storage_instance): data = {"key": "value"} - preprocessed_data = await storage_instance._apreprocess(preprocess=None, obj=data) + preprocessed_data = storage_instance._preprocess(obj=data, preprocess=None) assert preprocessed_data == data - async def fn(d): + def fn(d): d["foo"] = "bar" return d - preprocessed_data = await storage_instance._apreprocess(data, fn) + preprocessed_data = storage_instance._preprocess(obj=data, preprocess=fn) assert "foo" in preprocessed_data assert preprocessed_data["foo"] == "bar" diff --git a/tests/unit/test_threshold_optimizer_utility.py b/tests/unit/test_threshold_optimizer_utility.py new file mode 100644 index 00000000..61010fc3 --- /dev/null +++ b/tests/unit/test_threshold_optimizer_utility.py @@ -0,0 +1,77 @@ +import sys + +import pytest + +if sys.version_info.major == 3 and sys.version_info.minor < 10: + pytest.skip("Test requires Python 3.10 or higher", allow_module_level=True) + +from ranx import evaluate + +from redisvl.utils.optimize import LabeledData +from redisvl.utils.optimize.cache import _generate_run_cache +from redisvl.utils.optimize.utils import _format_qrels + +# Note: these tests are not intended to test ranx but to test that our data formatting for the package is correct + + +def test_known_precision_case(): + """ + Test case with known precision value. + + Setup: + - 2 queries + - Query 1 expects doc1, gets doc1 and doc2 (precision 0.5) + - Query 2 expects doc3, gets doc3 (precision 1.0) + Expected overall precision: 0.75 + """ + # Setup test data + test_data = [ + LabeledData( + query="test query 1", + query_match="doc1", + response=[ + {"id": "doc1", "vector_distance": 0.2}, + {"id": "doc2", "vector_distance": 0.3}, + ], + ), + LabeledData( + query="test query 2", + query_match="doc3", + response=[ + {"id": "doc3", "vector_distance": 0.2}, + {"id": "doc4", "vector_distance": 0.8}, + ], + ), + ] + + # Create qrels (ground truth) + qrels = _format_qrels(test_data) + + threshold = 0.4 + run = _generate_run_cache(test_data, threshold) + + # Calculate precision using ranx + precision = evaluate(qrels, run, "precision") + assert precision == 0.75 # (0.5 + 1.0) / 2 + + +def test_known_precision_with_no_matches(): + """Test case where some queries have no matches.""" + test_data = [ + LabeledData( + query="test query 2", + query_match="", # Expecting no match + response=[], + ), + ] + + # Create qrels + qrels = _format_qrels(test_data) + + # Generate run with threshold that excludes all docs for first query + threshold = 0.3 + run = _generate_run_cache(test_data, threshold) + + # Calculate precision + precision = evaluate(qrels, run, "precision") + assert precision == 1.0 # (0.0 + 1.0) / 2 diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index a6e3dd02..59a1abd8 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -1,3 +1,5 @@ +import re +import sys from functools import wraps import numpy as np @@ -11,11 +13,30 @@ ) from redisvl.utils.utils import ( assert_no_warnings, + denorm_cosine_distance, deprecated_argument, deprecated_function, + norm_cosine_distance, ) +def test_norm_cosine_distance(): + input = 2 + expected = 0 + assert norm_cosine_distance(input) == expected + + +def test_denorm_cosine_distance(): + input = 0 + expected = 2 + assert denorm_cosine_distance(input) == expected + + +def test_norm_denorm_cosine(): + input = 0.6 + assert input == round(denorm_cosine_distance(norm_cosine_distance(input)), 6) + + def test_even_number_of_elements(): """Test with an even number of elements""" values = ["key1", "value1", "key2", "value2"] @@ -441,3 +462,59 @@ def old_func(): with pytest.warns(DeprecationWarning): old_func() + + def test_logging_configuration_not_overridden(self): + """Test that RedisVL imports don't override existing logging configurations.""" + import os + import subprocess + + # Get the path to the helper script relative to this test file + current_dir = os.path.dirname(os.path.abspath(__file__)) + helper_script = os.path.join(current_dir, "logger_interference_checker.py") + + # Run the helper script in a separate process + result = subprocess.run( + [sys.executable, helper_script], capture_output=True, text=True + ) + + # Extract the log lines + output_lines = result.stdout.strip().split("\n") + pre_import_line = "" + post_import_line = "" + + for line in output_lines: + if "PRE_IMPORT_FORMAT" in line: + pre_import_line = line + elif "POST_IMPORT_FORMAT" in line: + post_import_line = line + + # Check if we found both lines + assert pre_import_line, "No pre-import log message found" + assert post_import_line, "No post-import log message found" + + # Print for debugging + print(f"Pre-import format: {pre_import_line}") + print(f"Post-import format: {post_import_line}") + + # Check format preservation + # Look for bracketed logger name format + has_bracketed_logger_pre = "[app]" in pre_import_line + has_bracketed_logger_post = "[app]" in post_import_line + assert has_bracketed_logger_pre == has_bracketed_logger_post, ( + f"Logger format changed from {'bracketed' if has_bracketed_logger_pre else 'unbracketed'} " + f"to {'bracketed' if has_bracketed_logger_post else 'unbracketed'}" + ) + + # Look for file/line information + has_file_line_pre = bool(re.search(r"\[\w+\.py:\d+\]", pre_import_line)) + has_file_line_post = bool(re.search(r"\[\w+\.py:\d+\]", post_import_line)) + assert ( + has_file_line_pre == has_file_line_post + ), f"File/line format changed: was present before: {has_file_line_pre}, present after: {has_file_line_post}" + + # Check date format (RedisVL strips the date portion) + has_date_pre = bool(re.match(r"\d{4}-\d{2}-\d{2}", pre_import_line)) + has_date_post = bool(re.match(r"\d{4}-\d{2}-\d{2}", post_import_line)) + assert ( + has_date_pre == has_date_post + ), f"Date format changed: was present before: {has_date_pre}, present after: {has_date_post}" diff --git a/tests/unit/test_validation.py b/tests/unit/test_validation.py new file mode 100644 index 00000000..68933938 --- /dev/null +++ b/tests/unit/test_validation.py @@ -0,0 +1,692 @@ +""" +Tests for the RedisVL schema validation module. + +This module tests the core validation functionality: +1. Model generation from schemas +2. Field-specific validators +3. JSON path extraction +4. Validation of various field types +""" + +import re +from typing import Any, List, Optional, Tuple, Union + +import pytest + +from redisvl.schema import IndexSchema +from redisvl.schema.fields import FieldTypes, VectorDataType +from redisvl.schema.schema import StorageType +from redisvl.schema.type_utils import TypeInferrer +from redisvl.schema.validation import ( + SchemaModelGenerator, + extract_from_json_path, + validate_object, +) + +# -------------------- FIXTURES -------------------- + + +@pytest.fixture +def sample_hash_schema(): + """Create a sample schema with HASH storage for testing.""" + schema_dict = { + "index": { + "name": "test-hash-index", + "prefix": "test", + "key_separator": ":", + "storage_type": "hash", + }, + "fields": [ + {"name": "test_id", "type": "tag"}, + {"name": "title", "type": "text"}, + {"name": "rating", "type": "numeric"}, + {"name": "location", "type": "geo"}, + { + "name": "embedding", + "type": "vector", + "attrs": { + "algorithm": "flat", + "dims": 4, + "datatype": "float32", + "distance_metric": "cosine", + }, + }, + { + "name": "int_vector", + "type": "vector", + "attrs": { + "algorithm": "flat", + "dims": 3, + "datatype": "int8", + "distance_metric": "l2", + }, + }, + ], + } + return IndexSchema.from_dict(schema_dict) + + +@pytest.fixture +def sample_json_schema(): + """Create a sample schema with JSON storage for testing.""" + schema_dict = { + "index": { + "name": "test-json-index", + "prefix": "test", + "key_separator": ":", + "storage_type": "json", + }, + "fields": [ + {"name": "test_id", "type": "tag", "path": "$.test_id"}, + {"name": "user", "type": "tag", "path": "$.metadata.user"}, + {"name": "title", "type": "text", "path": "$.content.title"}, + {"name": "rating", "type": "numeric", "path": "$.metadata.rating"}, + { + "name": "embedding", + "type": "vector", + "path": "$.content.embedding", + "attrs": { + "algorithm": "flat", + "dims": 4, + "datatype": "float32", + "distance_metric": "cosine", + }, + }, + { + "name": "int_vector", + "type": "vector", + "path": "$.content.int_vector", + "attrs": { + "algorithm": "flat", + "dims": 3, + "datatype": "int8", + "distance_metric": "l2", + }, + }, + ], + } + return IndexSchema.from_dict(schema_dict) + + +@pytest.fixture +def valid_hash_data(): + """Sample valid data for testing HASH storage validation.""" + return { + "test_id": "doc1", + "title": "Test Document", + "rating": 4.5, + "location": "37.7749,-122.4194", + "embedding": b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # Bytes for HASH + "int_vector": b"\x01\x02\x03", # Bytes for HASH + } + + +@pytest.fixture +def valid_json_data(): + """Sample valid data for testing JSON storage validation.""" + return { + "test_id": "doc1", + "metadata": {"user": "user123", "rating": 4.5}, + "content": { + "title": "Test Document", + "embedding": [0.1, 0.2, 0.3, 0.4], # List for JSON + "int_vector": [1, 2, 3], # List for JSON + }, + } + + +# -------------------- TEST HELPERS -------------------- + + +def validate_field( + schema: IndexSchema, + field_name: str, + value: Any, + should_pass: bool, + error_text: Optional[str] = None, +) -> Tuple[bool, Optional[str]]: + """ + Helper function to validate a field value against a schema. + + Args: + schema: The schema to validate against + field_name: The name of the field to validate + value: The value to validate + should_pass: Whether validation should pass + error_text: Expected error text if validation should fail + + Returns: + Tuple of (validation_success, error_message) + """ + # Get model for schema + model_class = SchemaModelGenerator.get_model_for_schema(schema) + + # Create test data with minimal viable fields + test_data = {field_name: value} + + # Try to validate + try: + validated = model_class.model_validate(test_data) + + # If we got here, validation passed + success = True + error_msg = None + + except Exception as e: + # Validation failed + success = False + error_msg = str(e) + + # Check if result matches expectation + if success != should_pass: + print("ERROR", error_msg, flush=True) + print(validated, flush=True) + assert ( + success == should_pass + ), f"Validation {'passed' if success else 'failed'} but expected {'pass' if should_pass else 'fail'}" + + # Check error text if specified and validation failed + if not success and error_text and error_msg: + assert ( + error_text in error_msg + ), f"Error '{error_msg}' does not contain expected text '{error_text}'" + + return success, error_msg + + +# -------------------- CATEGORY 1: BASIC UNIT TESTS -------------------- + + +class TestSchemaModelGenerator: + """Tests for the SchemaModelGenerator class.""" + + @pytest.mark.parametrize("schema_type", ["hash", "json"]) + def test_get_model_for_schema( + self, schema_type, sample_hash_schema, sample_json_schema + ): + """Test generating a model from a schema.""" + # Select schema based on type + schema = sample_hash_schema if schema_type == "hash" else sample_json_schema + + # Get model for schema + model_class = SchemaModelGenerator.get_model_for_schema(schema) + + # Verify model name matches the index name + assert model_class.__name__ == f"{schema.index.name}__PydanticModel" + + # Verify model has expected fields + for field_name in schema.field_names: + assert field_name in model_class.model_fields + + def test_model_caching(self, sample_hash_schema): + """Test that models are cached and reused.""" + # Get model twice + model1 = SchemaModelGenerator.get_model_for_schema(sample_hash_schema) + model2 = SchemaModelGenerator.get_model_for_schema(sample_hash_schema) + + # Verify same instance + assert model1 is model2 + + @pytest.mark.parametrize( + "field_type,storage_type,expected_type", + [ + (FieldTypes.TEXT, StorageType.HASH, str), + (FieldTypes.TAG, StorageType.HASH, str), + (FieldTypes.NUMERIC, StorageType.HASH, Union[int, float]), + (FieldTypes.GEO, StorageType.HASH, str), + (FieldTypes.VECTOR, StorageType.HASH, bytes), + (FieldTypes.TEXT, StorageType.JSON, str), + (FieldTypes.TAG, StorageType.JSON, str), + (FieldTypes.NUMERIC, StorageType.JSON, Union[int, float]), + (FieldTypes.GEO, StorageType.JSON, str), + (FieldTypes.VECTOR, StorageType.JSON, List[float]), + ], + ) + def test_type_mapping(self, field_type, storage_type, expected_type): + """Test mapping Redis field types to Pydantic types.""" + + # Create a basic field of the specified type + class SimpleField: + def __init__(self, ftype): + self.type = ftype + # Add attrs for vector fields + if ftype == FieldTypes.VECTOR: + + class Attrs: + dims = 4 + datatype = VectorDataType.FLOAT32 + + self.attrs = Attrs() + + field = SimpleField(field_type) + field_type_result = SchemaModelGenerator._map_field_to_pydantic_type( + field, storage_type + ) + + assert field_type_result == expected_type + + def test_unsupported_field_type(self): + """Test that an error is raised for unsupported field types.""" + + # Create a dummy field with unsupported type + class DummyField: + type = "unsupported_type" + + # Mapping should raise ValueError + with pytest.raises(ValueError) as exc_info: + SchemaModelGenerator._map_field_to_pydantic_type( + DummyField(), StorageType.HASH + ) + + assert "Unsupported field type" in str(exc_info.value) + + +class TestJsonPathExtraction: + """Tests for JSON path extraction functionality.""" + + @pytest.mark.parametrize( + "path,expected_value", + [ + ("$.test_id", "doc1"), + ("$.metadata.user", "user123"), + ("$.metadata.rating", 4.5), + ("$.content.title", "Test Document"), + ("$.content.embedding", [0.1, 0.2, 0.3, 0.4]), + ("metadata.user", "user123"), # alternate format + ("$.nonexistent", None), # nonexistent path + ("$.metadata.nonexistent", None), # nonexistent nested path + ], + ) + def test_extract_from_json_path(self, valid_json_data, path, expected_value): + """Test extracting values using JSON paths.""" + assert extract_from_json_path(valid_json_data, path) == expected_value + + +# # -------------------- CATEGORY 2: PARAMETRIZED VALIDATOR TESTS -------------------- + + +class TestBasicFieldValidation: + """Tests for validating non-vector field types.""" + + @pytest.mark.parametrize( + "field_type,field_name,valid_values,invalid_values", + [ + # TEXT fields + ( + "text", + "title", + [("Test Document", None), ("123", None), ("", None)], + [(123, "string"), (True, "string"), ([], "string")], + ), + # TAG fields + ( + "tag", + "test_id", + [("doc1", None), ("123", None), ("abc,def", None), ("", None)], + [ + (123, "string"), + (True, "string"), + ([], "string"), + ([1, 2, 3], "string"), + ], + ), + # NUMERIC fields + ( + "numeric", + "rating", + [(5, None), (4.5, None), (0, None), (-1.5, None), ("5.3", None)], + [("high", "number"), (True, "boolean"), ([], "number")], + ), + # GEO fields + ( + "geo", + "location", + [ + ("0,0", None), + ("90,-180", None), + ("-90,180", None), + ("37.7749,-122.4194", None), + ], + [ + ("invalid_geo", "lat,lon"), + ("37.7749", "lat,lon"), + ("37.7749,", "lat,lon"), + (",122.4194", "lat,lon"), + ("91,0", "lat,lon"), # Latitude > 90 + ("-91,0", "lat,lon"), # Latitude < -90 + ("0,181", "lat,lon"), # Longitude > 180 + ("0,-181", "lat,lon"), # Longitude < -180 + (123, "string"), + (True, "string"), + ], + ), + ], + ) + def test_basic_field_validation( + self, sample_hash_schema, field_type, field_name, valid_values, invalid_values + ): + """ + Test validation of basic field types (text, tag, numeric, geo). + + This test consolidates previously separate tests for different field types. + """ + # Test valid values + for value, _ in valid_values: + validate_field(sample_hash_schema, field_name, value, True) + + # For GEO fields, also verify pattern + if field_type == "geo" and isinstance(value, str): + assert re.match(TypeInferrer.GEO_PATTERN.pattern, value) + + # Test invalid values + for value, error_text in invalid_values: + validate_field(sample_hash_schema, field_name, value, False, error_text) + + # For GEO fields, also verify pattern failure + if field_type == "geo" and isinstance(value, str): + assert not re.match(TypeInferrer.GEO_PATTERN.pattern, value) + + @pytest.mark.parametrize( + "test_case", + [ + # Valid cases for HASH storage (bytes) + { + "storage": StorageType.HASH, + "field_name": "embedding", + "value": b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + "valid": True, + "error_text": None, + "description": "Valid bytes for HASH storage", + }, + { + "storage": StorageType.HASH, + "field_name": "int_vector", + "value": b"\x01\x02\x03", + "valid": True, + "error_text": None, + "description": "Valid bytes for HASH storage (int vector)", + }, + # Invalid cases for HASH storage (trying to use lists) + { + "storage": StorageType.HASH, + "field_name": "embedding", + "value": [0.1, 0.2, 0.3, 0.4], + "valid": False, + "error_text": "bytes", + "description": "List not valid for HASH storage", + }, + # Valid cases for JSON storage (lists) + { + "storage": StorageType.JSON, + "field_name": "embedding", + "value": [0.1, 0.2, 0.3, 0.4], + "valid": True, + "error_text": None, + "description": "Valid list for JSON storage", + }, + { + "storage": StorageType.JSON, + "field_name": "int_vector", + "value": [1, 2, 3], + "valid": True, + "error_text": None, + "description": "Valid int list for JSON storage", + }, + # Invalid cases for JSON storage (trying to use bytes) + { + "storage": StorageType.JSON, + "field_name": "embedding", + "value": b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + "valid": False, + "error_text": "list", + "description": "Bytes not valid for JSON storage", + }, + # Dimension validation + { + "storage": StorageType.JSON, + "field_name": "embedding", + "value": [0.1, 0.2, 0.3], # Should be 4 dimensions + "valid": False, + "error_text": "dimensions", + "description": "Wrong dimensions for vector", + }, + # Type validation for int vectors + { + "storage": StorageType.JSON, + "field_name": "int_vector", + "value": [0.1, 0.2, 0.3], # Should be integers + "valid": False, + "error_text": "integer", + "description": "Float values in int vector", + }, + ], + ) + def test_vector_field_validation( + self, sample_hash_schema, sample_json_schema, test_case + ): + """Test validation of vector fields with storage-specific requirements.""" + # Select the appropriate schema based on storage type + schema = ( + sample_hash_schema + if test_case["storage"] == StorageType.HASH + else sample_json_schema + ) + + # Validate the field + validate_field( + schema, + test_case["field_name"], + test_case["value"], + test_case["valid"], + test_case["error_text"], + ) + + +class TestNestedJsonValidation: + """Tests for JSON path-based validation with nested structures.""" + + @pytest.mark.parametrize( + "test_case", + [ + # Complete valid data + { + "data": { + "test_id": "doc1", + "metadata": {"user": "user123", "rating": 4.5}, + "content": { + "title": "Test Document", + "embedding": [0.1, 0.2, 0.3, 0.4], + "int_vector": [1, 2, 3], + }, + }, + "expected_fields": [ + "test_id", + "user", + "title", + "rating", + "embedding", + "int_vector", + ], + "missing_fields": [], + }, + # Partial data - missing some fields + { + "data": { + "test_id": "doc1", + "metadata": {"user": "user123"}, + "content": {"title": "Test Document"}, + }, + "expected_fields": ["test_id", "user", "title"], + "missing_fields": ["rating", "embedding", "int_vector"], + }, + # Minimal data + { + "data": {"test_id": "doc1"}, + "expected_fields": ["test_id"], + "missing_fields": [ + "user", + "title", + "rating", + "embedding", + "int_vector", + ], + }, + ], + ) + def test_nested_json_validation(self, sample_json_schema, test_case): + """Test validating nested JSON with various data structures.""" + # Validate object + validated = validate_object(sample_json_schema, test_case["data"]) + + # Verify expected fields are present + for field in test_case["expected_fields"]: + assert field in validated + + # Verify missing fields are not present + for field in test_case["missing_fields"]: + assert field not in validated + + +class TestEndToEndValidation: + """End-to-end tests for complete object validation against schema.""" + + @pytest.mark.parametrize( + "schema_type,data,expected_result", + [ + # Valid HASH data + ( + "hash", + { + "test_id": "doc1", + "title": "Test Document", + "rating": 4.5, + "location": "37.7749,-122.4194", + "embedding": b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + "int_vector": b"\x01\x02\x03", + }, + { + "success": True, + "fields": [ + "test_id", + "title", + "rating", + "location", + "embedding", + "int_vector", + ], + }, + ), + # Partial HASH data + ( + "hash", + {"test_id": "doc1", "title": "Test Document"}, + {"success": True, "fields": ["test_id", "title"]}, + ), + # Valid JSON data + ( + "json", + { + "test_id": "doc1", + "metadata": {"user": "user123", "rating": 4.5}, + "content": { + "title": "Test Document", + "embedding": [0.1, 0.2, 0.3, 0.4], + "int_vector": [1, 2, 3], + }, + }, + { + "success": True, + "fields": [ + "test_id", + "user", + "rating", + "title", + "embedding", + "int_vector", + ], + }, + ), + # Invalid HASH data - wrong vector type + ( + "hash", + { + "test_id": "doc1", + "embedding": [0.1, 0.2, 0.3, 0.4], # Should be bytes for HASH + }, + {"success": False, "error_field": "embedding"}, + ), + # Invalid JSON data - wrong vector type + ( + "json", + { + "test_id": "doc1", + "content": { + "embedding": b"\x00\x00\x00\x00" # Should be list for JSON + }, + }, + {"success": False, "error_field": "embedding"}, + ), + ], + ) + def test_end_to_end_validation( + self, sample_hash_schema, sample_json_schema, schema_type, data, expected_result + ): + """Test validating complete objects with various data scenarios.""" + # Select schema based on type + schema = sample_hash_schema if schema_type == "hash" else sample_json_schema + + if expected_result["success"]: + # Validation should succeed + validated = validate_object(schema, data) + + # Verify expected fields are present + for field in expected_result["fields"]: + assert field in validated + else: + # Validation should fail + with pytest.raises(ValueError) as exc_info: + validate_object(schema, data) + + # Error should mention the field + assert expected_result["error_field"] in str(exc_info.value) + + +# -------------------- ADDITIONAL TESTS -------------------- + + +class TestEdgeCases: + """Tests for edge cases and boundary conditions.""" + + def test_empty_object_validation(self, sample_hash_schema, sample_json_schema): + """Test validating an empty object.""" + # Empty object should validate for both storage types (all fields are optional) + # TODO confirm if this is indeed true + assert validate_object(sample_hash_schema, {}) == {} + assert validate_object(sample_json_schema, {}) == {} + + def test_additional_fields(self, sample_hash_schema, valid_hash_data): + """Test that additional fields not in schema are NOT ignored.""" + # Add extra field not in schema + data_with_extra = valid_hash_data.copy() + data_with_extra["extra_field"] = "some value" + + # Validation should succeed and ignore extra field + validated = validate_object(sample_hash_schema, data_with_extra) + assert "extra_field" in validated + + def test_explicit_none_fields_excluded(self, sample_hash_schema): + """Test that fields explicitly set to None are excluded.""" + # Data with explicit None values + data = { + "test_id": "doc1", + "title": "Test Document", + "rating": None, + "location": None, + } + + # Validate and check fields + validated = validate_object(sample_hash_schema, data) + assert "test_id" in validated + assert "title" in validated + assert "rating" not in validated + assert "location" not in validated